C ++链接和模板专业化 [英] C++ linking and template specializations

查看:119
本文介绍了C ++链接和模板专业化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究有关模板专业化的C ++链接器的行为.我正在使用Microsoft Visual C ++ 2010进行这些实验.我不知道其他工具链(例如gcc)的行为是否相同.

I'm studying the behavior of the C++ linker with respect to template specializations. I'm using Microsoft Visual C++ 2010 for these experiments. I don't know if the behavior is the same with other toolchains (e.g. 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.

Expectedly, this code doesn't link because foo<double>() has multiple definitions as it gets instantiated once in bar.cpp and once in main.cpp (via specialization). We would then expect, if this program would link, that bar() and main() would use distinct instantiations of foo() such that at the end we would have x == 1 and y == 2.

通过将foo<double>()的特殊化声明为static:来修复链接错误:

Let's fix the link error by declaring the specialization of 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
}

我们现在有x  ==  1和y  ==  2. (注意:我们必须在此处使用static关键字:匿名名称空间将不会使用,因为我们不能将模板函数专用于不同于其声明的名称空间.)

We now have x == 1 and y == 2, as we expected. (Note: we must use the static keyword here: an anonymous namespace won't do since we can't specialize a template function in a different namespace than its declaration.)

现在,使用static关键字非常不直观.通常,特殊化foo<double>()会驻留在头文件中的某个位置,因此将被标记为内联,如以下代码段所示:

Now, the use of the static keyword is rather unintuitive. Typically, the specialization foo<double>() would reside somewhere in a header file, and thus would be marked as inline, like in the following snippet:

// 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在此代码中是什么意思?

This code now links properly and when we run it we get x == 2 and y == 2. This is the bit I find surprising: why is there a single definition of foo<double>()? What is the meaning of inline in this code?

最后一个片段:

// 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 .

This case is actually not surprising: the specialization of foo<double>() is no longer instantiated in main.cpp (although the declaration is still there), so the only instantiation remaining is the one in bar.cpp.

推荐答案

您实际上违反了C ++规则(强调我的意思):

You are actually violating a C++ rule here (emphasis mine):

14.7.3 [temp.expl.spec] :

6/如果一个模板,一个成员模板或一个类模板的成员是显式专门化的,则应在首次使用该专门化之前声明该专门化,这将导致采用隐式实例化在发生这种使用的每个翻译单元中的位置; 无需诊断.如果程序 没有为显式专业化提供定义,或者以某种方式使用专业化会导致隐式实例化,或者成员是虚拟成员函数,程序格式错误,不需要诊断.对于已声明但未定义的显式专门化,永远不会生成隐式实例化. [示例:

6/ If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit specialization that is declared but not defined. [ Example:

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

示例]

这篇关于C ++链接和模板专业化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆