【问题标题】:Separate class template declaration and specialization单独的类模板声明和特化
【发布时间】:2019-11-21 06:49:01
【问题描述】:

我有一个类模板的声明。假设此模板的特化从具有特定字符串 id 的数据库中保存和加载对象。我只需要将此模板用于特定类型,并将这些专业化放入不同的 cpp 文件中。

// TemplateInterface.h
template <typename T>
struct Foo
{
    static void save(T const& v);
    static T load();
};

假设我使用此代码保存 std::tuple 的值。

// TemplateInterface.cpp
template <>
struct __declspec(dllexport) Foo<Class1>
{
    static void save(Class1 const& v)
    {
        MyDatabase::save("class1-id", v);
    }
    static Class1 load()
    {
        return MyDatabase::load("class1-id");
    }
};

template <>
struct __declspec(dllexport) Foo<Class2>
{
    static void save(Class2 const& v)
    {
        MyDatabase::save("class2-id", v);
    }
    static Class2 load()
    {
        return MyDatabase::load("class2-id");
    }
};

如您所见,唯一的区别是字符串 ID。 (没有 __declspec(dllexport) 此代码不起作用。) 我发现,我可以在 single 文件中这样做:

// Source.cpp
template <typename T, typename Traits>
struct Helper
{
    static void save(T const& v)
    {
        MyDatabase::save(Traits::getID(), v);
    }
    static T load()
    {
        return MyDatabase::load(Traits::getID());
    }
};


template <typename T>
struct Foo
{
    static void save(T const& v);
    static T load();
};

struct Class1Traits 
{
    static std::string getID() { return "class1-id"; }
};

struct Class2Traits
{
    static std::string getID() { return "class2-id"; }
};

template<>
struct Foo<Class1> : Helper<Class1, Class1Traits> {};

template<>
struct Foo<Class2> : Helper<Class2, Class2Traits> {};

但是当我在不同的文件中执行此操作时(TemplateInterface.h 中的声明和 TemplateInterface.cpp 中的专门化),我得到链接器错误:

error LNK2019: unresolved external symbol "public: static Class1__cdecl Foo<Class1>::load()" referenced in function _main

其他方法的错误相同。 添加 dllexport 并使用没有帮助。 你能帮帮我吗?

我知道的唯一解决方案是从模板规范中显式调用基类方法。但这是一个奇怪的解决方案。

【问题讨论】:

    标签: c++ templates linker dllexport


    【解决方案1】:

    模板实例化与模板特化不同。

    例如,拿这段代码,告诉我会发生什么:

    // TemplateInterface.h
    template <typename T>
    struct Foo
    {
        static T load();
    };
    
    int main() {
        int foo = Foo<int>::load();
    }
    

    然后,将其添加到一个单独的、已编译的 cpp 中:

    template <>
    struct Foo<int>
    {
        static int load_special()
        {
            return 0;
        }
    };
    

    如您所见,根据专业化,没有Foo&lt;int&gt;::load(),而只有Foo&lt;int&gt;::load_special()

    您的代码实际上违反了 ODR。

    如果您希望专门化您的模板并且想要将专门化隐藏到它自己的 cpp 文件中,您必须告诉编译器您的类型存在专门化以防止它实例化主模板。

    这是通过向前声明你的专业来完成的:

    template<>
    struct Foo<int>; // specialization elsewhere!
    

    【讨论】:

    • 我应该在哪里提出这个声明?
    • 在你的标题中,告诉任何包含它的编译器在某处有专门化
    猜你喜欢
    • 2011-01-30
    • 1970-01-01
    • 2019-08-02
    • 1970-01-01
    • 2021-12-18
    • 1970-01-01
    • 2015-05-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多