C++链接和模板特化

Fra*_*une 12 c++ linker templates inline specialization

我正在研究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
}
Run Code Online (Sandbox Code Playgroud)

预计,此代码不会链接,因为它foo<double>()有多个定义,因为它在bar.cpp中实例化一次,在main.cpp中实例化一次(通过专门化).如果这个程序将链接,那么我们会期望bar()并且main()将使用不同的实例,foo()这样最终我们将得到x == 1和y == 2.

让我们通过声明foo<double>()as 的特化来修复链接错误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
}
Run Code Online (Sandbox Code Playgroud)

我们现在有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
}
Run Code Online (Sandbox Code Playgroud)

这段代码现在正确链接,当我们运行它时,我们得到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
}
Run Code Online (Sandbox Code Playgroud)

这种情况实际上并不令人惊讶:main.cpp中foo<double>()不再实例化特殊化(尽管声明仍然存在),因此唯一的实例化是bar.cpp中的实例化.

Mat*_* M. 14

你实际上是在违反C++规则(强调我的):

14.7.3 [temp.expl.spec]:

6 /如果一个模板,一个成员模板或一个类模板的成员被明确地专门化,那么该特化应该在第一次使用该特化之前声明,这将导致隐式实例化发生,在每个翻译单元中使用发生; 无需诊断.如果程序没有提供显式特化的定义,并且特殊化的使用方式会导致隐式实例化或成员是虚拟成员函数,则程序格式错误,无需诊断.永远不会为已声明但未定义的显式特化生成隐式实例化.[ 例如:

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
Run Code Online (Sandbox Code Playgroud)

- 结束例子 ]