C ++链接和模板特化

时间:2011-08-25 12:34:29

标签: c++ templates linker 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
}

预计此代码不会链接,因为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

1 个答案:

答案 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
  

- 结束示例]