我正在研究C ++链接器在模板特化方面的行为。我正在使用Microsoft Visual C ++ 2010进行这些实验。我不知道其他工具链(例如gcc)的行为是否相同。
这是第一个代码段:
// bar.cpp
template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }
// main.cpp
template <typename T> int foo() { return 1; }
template <> int foo<double>() { return 2; }
int bar();
int main()
{
const int x = bar();
const int y = foo<double>(); // doesn't link
}
预计此代码不会链接,因为foo<double>()
有多个定义,因为它在 bar.cpp 中实例化一次,而在 main.cpp 中实例化一次(通过专业化)。我们希望,如果此程序将链接,bar()
和main()
将使用foo()
的不同实例化,以便最后我们将x = = 1且y == 2.
让我们通过将foo<double>()
的专业化声明为static
来解决链接错误:
// bar.cpp
template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }
// main.cpp
template <typename T> int foo() { return 1; }
template <> static int foo<double>() { return 2; } // note: static
int bar();
int main()
{
const int x = bar(); // x == 1
const int y = foo<double>(); // y == 2
}
我们现在有x == 1和y == 2,正如我们预期的那样。 (注意:我们必须在这里使用static
关键字:匿名命名空间不会这样做,因为我们不能在与其声明不同的命名空间中专门化模板函数。)
现在,static
关键字的使用相当不直观。通常,专门化foo<double>()
将驻留在头文件中的某个位置,因此将标记为内联,如下面的代码段所示:
// bar.cpp
template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }
// main.cpp
template <typename T> int foo() { return 1; }
template <> inline int foo<double>() { return 2; } // note: inline
int bar();
int main()
{
const int x = bar(); // x == 2
const int y = foo<double>(); // y == 2
}
此代码现在正确链接,当我们运行它时,我们得到x == 2和y == 2. 这是我感到惊讶的一点:为什么foo<double>()
有一个单一的定义?这段代码中inline
的含义是什么?
最后一段:
// bar.cpp
template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }
// main.cpp
template <typename T> int foo() { return 1; }
template <> inline int foo<double>() { return 2; } // note: inline
int bar();
int main()
{
const int x = bar(); // x == 1
// const int y = foo<double>(); // note: commented out
}
这种情况实际上并不令人惊讶:foo<double>()
的特化不再在 main.cpp 中实例化(尽管声明仍然存在),因此剩下的唯一实例就是一个在 bar.cpp 。
答案 0 :(得分:14)
你实际上违反了C ++规则(强调我的):
14.7.3 [temp.expl.spec] :
6 / 如果模板,成员模板或类模板的成员是显式专用的,则应在首次使用该特化之前声明该特化,这将导致隐式实例化地方,在发生这种用途的每个翻译单位; 无需诊断。如果是程序 不提供显式特化的定义,并且特殊化的使用方式会导致隐式实例化或成员是虚拟成员函数,程序格式错误,无需诊断< / strong>即可。永远不会为已声明但未定义的显式特化生成隐式实例化。 [示例:
class String { };
template<class T> class Array { /* ... */ };
template<class T> void sort(Array<T>& v) { /* ... */ }
void f(Array<String>& v) {
sort(v); // use primary template
// sort(Array<T>&), T is String
}
template<> void sort<String>(Array<String>& v); // error: specialization
// after use of primary template
template<> void sort<>(Array<char*>& v); // OK: sort<char*> not yet used
template<class T> struct A {
enum E : T;
enum class S : T;
};
template<> enum A<int>::E : int { eint }; // OK
template<> enum class A<int>::S : int { sint }; // OK
template<class T> enum A<T>::E : T { eT };
template<class T> enum class A<T>::S : T { sT };
template<> enum A<char>::E : int { echar }; // ill-formed,
// A<char>::E was instantiated
// when A<char> was instantiated
template<> enum class A<char>::S : int { schar }; // OK
- 结束示例]