C++ 模板专门化歧义:可以链接专门化方法或原始方法

C++ 模板专门化歧义:可以链接专门化方法或原始方法,c++,templates,specialization,gcc4,C++,Templates,Specialization,Gcc4,查看tps.hpp中的base::m_visible方法(它是«默认»): 并从tps_main.cpp进一步调用: 这只有在优化-O3时才会发生。任何带有注释的实验版本都会产生不同的结果为什么?因为这是未定义的行为 如果模板是专门化的,则此专门化必须随处可见 在一个翻译单元中声明并实现专门化。这个专门化的模板声明基本上完全在该翻译单元内部,并且专门化只对该翻译单元可见。您的tps_main翻译单元对模板专业化没有可见性,也没有任何知识;因此,将其与另一个模板专用化可见的翻译单元链接在一起会导致

查看tps.hpp中的
base::m_visible
方法(它是«默认»):

并从tps_main.cpp进一步调用:


这只有在优化
-O3
时才会发生。任何带有注释的实验版本都会产生不同的结果为什么?

因为这是未定义的行为

如果模板是专门化的,则此专门化必须随处可见

在一个翻译单元中声明并实现专门化。这个专门化的模板声明基本上完全在该翻译单元内部,并且专门化只对该翻译单元可见。您的
tps_main
翻译单元对模板专业化没有可见性,也没有任何知识;因此,将其与另一个模板专用化可见的翻译单元链接在一起会导致未定义的行为

您需要在头文件中声明并定义(这里有一些不太相关的例外情况)专门化,这样每个包含有模板定义的头文件的翻译单元也将有专门化的定义

对于专用模板实例,模板专用化不是某种形式的模板的“实现”。模板专门化基本上是一个完全独立的类声明。因此,如果您有一个使用该类的翻译单元,那么它必须声明该类

您不能只在一个翻译单元中声明一个类,而期望在另一个翻译单元中使用它,而不让另一个翻译单元看到该声明

基本上,要链接在一起的所有翻译单元必须具有相同的类、对象和模板声明。这包括模板专门化


这只是一种迂回的说法。

不,您需要到处声明专门化,但只要某个地方有一个专门化定义(和实例化),就可以了。对无休止的冗余定义进行过度、浪费的重新编译是最方便的是问题的决定
#include <sstream>
#include <iostream>

template <typename T> struct base
{
  void m_log() const;
  void m_visible() const;
};

struct inheritor: base<inheritor>
{
  void log() const;
};

template <typename T> void base<T>::m_log() const
{
  std::ostringstream os;
  m_visible();
  os.str(""); // We have «default/default» if comment here, :-0
  std::cout << os.str();
}

template <typename T> void base<T>::m_visible() const
{
  std::cout
    << "default" << std::endl
  ;
}
#include "tps.hpp"

template <> void base<inheritor>::m_visible() const
{
  std::cout
    << "spec" << std::endl
  ;
}

void inheritor::log() const
{
  m_log();
}
#include "tps.hpp"

int main(int, char **argv)
{
  std::cout << argv[0] << ": ";
  inheritor().m_log(); // We have «spec/spec» if call inheritor::log
  return 0;
}
g++ -Wall -O3 -o tps1 tps_spec.cpp tps_main.cpp && g++ -Wall -O3 -o tps2 tps_main.cpp tps_spec.cpp && ./tps1 ; ./tps2
./tps1: spec
./tps2: default