C++ 使用g++;

C++ 使用g++;,c++,templates,template-specialization,C++,Templates,Template Specialization,使用编译时,为什么以下代码不起作用: $ g++ temp_main.cpp temp_spec.cpp /tmp/ccirjc3Y.o:temp_spec.cpp:(.text+0x100): multiple definition of `my::say()' /tmp/ccSo7IVO.o:temp_main.cpp:(.text$_ZN2myILi0EE3sayEv[my::say()]+0x0): first defined here collect2: ld returned 1 e

使用编译时,为什么以下代码不起作用:

$ g++ temp_main.cpp temp_spec.cpp /tmp/ccirjc3Y.o:temp_spec.cpp:(.text+0x100): multiple definition of `my::say()' /tmp/ccSo7IVO.o:temp_main.cpp:(.text$_ZN2myILi0EE3sayEv[my::say()]+0x0): first defined here collect2: ld returned 1 exit status temp\u main.cpp:

#ifndef temp_gen_h
#define temp_gen_h

#include <iostream>

template<int N>
struct my
{
   static void say();
};

template<int N>
void my<N>::say()
{
   std::cout << "generic " << N << std::endl;
}

#endif
#ifndef temp_spec_h
#define temp_spec_h

#include <iostream>

#include "temp_gen.h"

template<>
void my<0>::say()
{
   std::cout << "specialized " << 0 << std::endl;
}

#endif
#include "temp_spec.h"
#include "temp_gen.h"

int main(int argc, char* argv[])
{
   my<0>::say();  //should say "specialized 0"
   my<1>::say();  //should say "generic 0"
}
#包括“临时发电机h”
int main(int argc,char*argv[])
{
my::say();//应该说“0”
my::say();//应该是“泛型0”
}

我认为,由于模板专门化的实现在标题中,因此需要将其标记为
内联
,以便编译器/链接器知道您可以违反一个定义规则。

main.cpp
中,您没有专门化模板类,这是因为您没有包括
temp_spec.h

正如沃恩·卡托(Vaughn Cato)所指出的(参见注释),您应该将专用方法(不再是模板方法)的定义移动到
temp_spec.cpp

我认为(但我不是这方面的专家)您应该始终将专门化放在通用模板的正下方(在相同的头文件中),因为无论何时包含此内容,您都希望定义专门化的模板,否则在发生此类错误时您会感到困惑。但是,您可以只在
temp\u gen.h
的底部包含
temp\u spec.h


此外,模板头不需要.cpp文件,因为永远不会有任何代码需要单独编译(模板类总是在使用它的另一个.cpp文件中编译,我认为链接时会删除重复的模板类,在链接时会造成麻烦,有一次你没有专门化它,有一次你这样做了)[本段仅适用于通用模板类,不适用于(完全)特殊类,因为它们需要在自己的编译单元中包含一些代码,请参见上面的注释和编辑。]

使用n3337版本的标准(重点):

14.7.3明确的专业化[临时解释规范]

6/如果一个模板、一个成员模板或一个类模板的一个成员被显式地专门化,那么该专门化应在第一次使用该专门化之前声明,该专门化将导致隐式实例化发生,在发生此类使用的每个翻译单元中;不重新进行诊断要求

由于在
main
中,
my::say
的专门化不可见,因此会发生隐式实例化,最终出现上述情况:无需诊断(来自编译器)


请注意,只有在声明了泛型对应项之后才能声明专门化。

我将内联放在say实现的前面,但调用时程序输出泛型0、泛型1,并且它应该是专门化的0、泛型1。我认为在定义某些模板内容时,已经允许违反一条定义规则了吗因为它需要为所有编译单独定义(因为模板定义不知道是针对哪些类型
T
进行编译,我们才真正将其用于特定类型),并在链接时合并。这至少是我理解其工作原理的方式…@ommittones:由于您没有在temp_main.cpp中包含temp_spec.h,因此专用化不可见,因此主函数使用泛型定义。原因是编译器在使用模板的对象文件中实例化模板;链接器只是遍历并合并重复的实例。在这里,链接器很混乱,因为
my::say()
的一个实例出现在两个不同的对象文件中,并且它们不匹配。@MikeDeSimone哦,我在阅读您的评论之前刚刚添加了这个;)不仅是模板类,还有模板化的任何内容。规则是专门化只需要在模板声明之后。因此,您可以在
template void my::say(){…}
之前定义
template void my::say(){…}
模板类my{…static void say();…}模板表示显式专门化。显式专门化不是模板,它们不应该在头文件中。@leems:最好将类模板和相关专门化放在一起。但有些专业可能并不相关。例如,使用traits样式模板。只是如果你在同一个头文件中放了一个相关的专门化,并且它是一个显式的专门化,那么只有声明应该在头文件中,或者它应该是内联的。