首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将模板的部分提取到cpp文件中。

将模板的部分提取到cpp文件中。
EN

Stack Overflow用户
提问于 2022-05-31 08:10:29
回答 4查看 90关注 0票数 1

在构建类模板时,通常需要将整个模板放在头文件中,这是不好的,因为严重模板的代码很难解释,编译和生成低于标准的错误消息需要很长时间。但是,通常情况下,模板类中与模板直接相关的部分--参数可以被分离成几个小函数,其余代码可以是常规代码,在需要时调用这些函数:

代码语言:javascript
复制
#include <cstddef>

template <class T>
struct Array {
    std::byte* data;
    std::size_t size;
    std::size_t capacity;

    void construct_at(void* p); // 
    void destruct_at(void* p);  // Directly type-related
    size_t obj_size();          // 


    void push_back() {                              // 
        if (size == capacity) {                     //
            // ... resize ...                       //
        }                                           //
        construct_at(data + size * obj_size());     //
        ++size;                                     // Not directly
    }                                               // type related
                                                    //
    void pop_back() {                               //
        auto const off = (size - 1) * obj_size();   //
        destruct_at(data + off);                    //
        --size;                                     //
    }                                               //
};

有没有一种方法可以将这个类中与类型(push_back、pop_back)不直接相关的部分提取到非模板代码中,这些代码碰巧调用模板化函数(construct_at、destruct_at、obj_size),这些函数以某种方式对当前的类型执行正确的操作?

理论上,这可以通过虚拟函数来实现:

代码语言:javascript
复制
#include <cstddef>

struct ArrayBase {
    std::byte* data;
    std::size_t size;
    std::size_t capacity;

    virtual void construct_at(void* p) = 0; // 
    virtual void destruct_at(void* p) = 0;  // pure-virtual
    virtual size_t obj_size() = 0;          // 


    void push_back(); //
    void pop_back();  // defined in a cpp file
};


template <class T>
struct Array : ArrayBase {
    void construct_at(void* p) override final {
        new (p) T;
    }

    void destruct_at(void* p) override final {
        static_cast<T*>(p)->~T();
    }

    size_t obj_size() override final {
        return sizeof(T);
    }
};

这里的主要问题是,我们需要通过一个虚拟表。但是在我们的代码中,没有运行时的动态性,应该可以避免这种间接性。是这样吗?

EN

回答 4

Stack Overflow用户

发布于 2022-05-31 12:14:05

有一种解决方案不是很通用,但有时适用于创建一个直接与字节一起工作的类的非类型安全版本,然后创建一个环绕复杂逻辑的类型安全版本:

代码语言:javascript
复制
#include <cstddef>

struct ArrayBase {
private:
    std::byte* data;
    std::size_t size_in_byes;
    std::size_t capacity_in_bytes;

protected:

    void* push_back(size_t bytes);  // Complicated logic is
    void* pop_back(size_t bytes);   // moved to a .cpp file
};

template <class T>
struct Array : private ArrayBase {
    void push_back() {
        auto p = push_back(sizeof(T));
        new (p) T;
    }

    void pop_back() {
        auto p = pop_back(sizeof(T));
        static_cast<T*>(p)->~T();
    }
};

如果非类型安全版本比类型安全版本要复杂得多,这是可行的.

票数 1
EN

Stack Overflow用户

发布于 2022-05-31 14:33:15

如果我对您的理解是正确的,那么您就会怀疑ArrayBase::push_back()是否能够避免通过虚拟表调用Array::construct_at()。但是,如果目标是减少标题的大小并提高编译时间,那么这是一个明显的否定,除非您使用LTO (链接时间优化)。

如果您在它自己的编译单元中有push_back(),并且只编译它一次,那么编译器就无法知道在编译push_back()时调用哪个版本的construct_at是正确的。编译器也无法在使用push_back()并知道T类型时内联对它的调用。

这方面的例外是当您使用LTO时,因为那时整个源基本上在链接时间变成一个编译单元,所有类型的程序优化都可能发生,包括将虚拟函数调用演绎和消除为静态调用(或插入所述调用)。

如果将整个代码留在标头中,则类似。但是如果分割出非模板的部分又有什么意义呢?

票数 1
EN

Stack Overflow用户

发布于 2022-05-31 11:16:12

一种更简单的提取方法是当方法实际上与模板无关时。

您可以尝试以多种方式删除依赖项以进行键入。

正如您所做的那样,virtual或尝试函数指针,类似于:

代码语言:javascript
复制
#include <cstddef>

struct ArrayBase {
    std::byte* data;
    std::size_t size;
    std::size_t capacity;

#define OPTION moreReadableOption
#if OPTION == 1
    void push_back(void (*construct_at)(void*));
    void pop_back(void (*destruct_at)(void*));
#elif OPTION == 2
    void push_back(std::type_identity_t<void(void*)>* construct_at);
    void pop_back(std::type_identity_t<void(void*)>* destruct_at);
else
    using construct_at_func = void(void*);
    using destruct_at_func = void(void*);

    void push_back(construct_at_func* construct_at);
    void pop_back(destruct_at_func* destruct_at);
#endif

};

template <class T>
struct Array : ArrayBase {
    static void construct_at(void* p) { new (p) T; }
    static void destruct_at(void* p) { static_cast<T*>(p)->~T(); }
    static size_t obj_size() { return sizeof(T); }

    void push_back() { ArrayBase::push_back(&construct_at); }
    void pop_back() { ArrayBase::pop_back(&destruct_at); }
};

cpp文件:

代码语言:javascript
复制
void ArrayBase::push_back(void (*construct_at)(void*))
{
    if (size == capacity) {
        // ... resize ...
    }
    construct_at(data + size * obj_size());
    ++size;
}

void ArrayBase::pop_back(void (*destruct_at)(void*)) {
    auto const off = (size - 1) * obj_size();
    destruct_at(data + off);
    --size;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72444273

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档