这是[问题]的后续行动:No generated code for explicitly specialized template even with explicit instantiation .
我在.cpp文件中使用部分特化来处理特殊情况,同时不强制所有代码进入头文件 . 下面给出了我正在做的一个简化示例 . 这适用于gcc和clang,但考虑到上面问题的答案,我想知道我是否只是幸运的是链接器正在寻找兼容的符号 .
我从foo.hpp中定义的类模板开始:
#pragma once
template <typename T1, typename T2>
class foo
{
public:
foo (T1 t1, T2 t2) : d_t1(t1), d_t2(t2) {}
~foo () = default;
void print ();
private:
T1 d_t1;
T2 d_t2;
};
请注意,print()方法已声明,但未定义 .
现在在foo.cpp中,我定义了print()方法 .
#include <iostream>
template <typename T1, typename T2>
void
foo<T1,T2>::print()
{
std::cout << "T1 is: ";
d_t1.print();
std::cout << std::endl;
std::cout << "T2 is: ";
d_t2.print();
std::cout << std::endl << std::endl;
}
请注意,此实现假定T1和T2都有一个名为print()的方法 . 在我的真实世界代码中,用作此模板的参数的类型通常符合此接口,但有时它们不符合,我通过.cpp文件中的部分特化来处理它 . 此模板不适用于真正的通用用法,它只需要使用我预先知道的少量类型 .
例如,我可能有3种类型,如
class HasPrint
{
public:
HasPrint () = default;
~HasPrint () = default;
void print ();
};
class AlsoHasPrint
{
public:
AlsoHasPrint () = default;
~AlsoHasPrint () = default;
void print ();
};
class NoPrint
{
public:
NoPrint () = default;
~NoPrint () = default;
void noPrint ();
};
请注意,'HasPrint'和'AlsoHasPrint'类具有print()方法,而'NoPrint'类具有noPrint()方法 . 在这些类的主体中,print / noPrint方法只是打印类的名称 .
为了处理使用NoPrint作为模板参数之一的人的情况,我在foo.cpp文件中定义了一个部分特化:
template <typename T2>
class foo <NoPrint, T2>
{
public:
foo (NoPrint n1, T2 t2) : d_n1(n1), d_t2(t2) {}
~foo () = default;
void print ()
{
std::cout << "NoPrint is: ";
d_n1.noPrint();
std::cout << std::endl;
std::cout << "T2 is: ";
d_t2.print();
std::cout << std::endl;
}
private:
NoPrint d_n1;
T2 d_t2;
};
然后为了获得我需要为我使用的所有排列生成的所有代码,我包括(在foo.cpp文件中)foo的以下显式实例化 .
template class foo<HasPrint, HasPrint>;
template class foo<HasPrint, AlsoHasPrint>;
template class foo<AlsoHasPrint, AlsoHasPrint>;
template class foo<NoPrint, HasPrint>;
最后一次显式实例化的结果是使用NoPrint对象生成代码以扩展模板并调用方法noPrint() .
在一个单独的test.cpp文件中,我有我的测试程序的驱动程序 .
#include "foo.hpp"
#include "hasprint.hpp"
#include "alsohasprint.hpp"
#include "noprint.hpp"
int
main (int argc, char** argv)
{
HasPrint h1;
HasPrint h2;
AlsoHasPrint a1;
NoPrint n1;
foo<HasPrint, HasPrint> f1 (h1, h2);
foo<HasPrint, AlsoHasPrint> f2 (h1, a1);
foo<AlsoHasPrint, AlsoHasPrint> f3 (a1, a1);
foo<NoPrint, HasPrint> f4 (n1, h1);
f1.print();
f2.print();
f3.print();
f4.print();
}
这在gcc 4.8.3和clang 3.2上都可以正常工作 . 结果是:
T1 is: HasPrint
T2 is: HasPrint
T1 is: HasPrint
T2 is: AlsoHasPrint
T1 is: AlsoHasPrint
T2 is: AlsoHasPrint
NoPrint is: NoPrint
T2 is: HasPrint
这使得foo.hpp的头文保持良好和干净,代价是需要对我使用模板的每组类型使用显式实例化,如果其中一个参数类型不符合接口,则部分特化 .
鉴于我在测试驱动程序中使用了类型为Foo <NoPrint,HasPrint>,而没有通知编译器存在专门化,这是“格式良好”还是我很幸运?
谢谢
1 回答
根据[temp.class.spec],您的代码格式错误,无需诊断:
也就是说,对于这种情况,您根本不需要部分专业化,只需向您的成员转发:
哪里:
我会将这些类型特征的实现作为练习留给读者 . 有关如何编写此类内容的几种不同方法的指南,请参阅this question,其中包含涉及std::experimental::is_detected,void_t,overload resolution with trailing-return-type,can_apply和REQUIRES的解决方案 .