模板专业化歧义:可以链接专门的或原始的方法
Template specialization ambiguity: either specialized or original method may be linked
查看 tps.hpp 中的 base::m_visible
方法(这是“默认”):
#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
;
}
专门用于 tps_spec.cpp 中的 inheritor
结构(名为 «spec»):
#include "tps.hpp"
template <> void base<inheritor>::m_visible() const
{
std::cout
<< "spec" << std::endl
;
}
void inheritor::log() const
{
m_log();
}
并从 tps_main.cpp 进一步调用:
#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;
}
结果取决于编译单元的顺序 (GCC 4.8.4):
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
这只会在优化 -O3
时发生。任何带有注释标记的实验版本都会产生不同的结果。 为什么?
因为这是未定义的行为。
如果模板是特化的,则这种特化必须"visible"无处不在。
您在一个翻译单元中声明并实施了专业化。那个专门化的模板声明,基本上,完全在那个翻译单元内部,并且专门化只对那个翻译单元可见。您的 tps_main
翻译单元对模板专业化一无所知,也一无所知;因此,将其与具有此模板特化可见的不同翻译单元链接在一起会导致未定义的行为。
你需要在头文件中声明并定义(有一些例外情况在这里不是很相关),所以每个包含带有模板定义的头文件的翻译单元也将具有专业化的定义。
模板特化不是某种模板的 "implementation",用于特定模板实例。模板专业化基本上是一个完全独立的 class 声明。因此,如果您有一个使用 class 的翻译单元,它必须声明 class。
您不能只在一个翻译单元中声明 class,然后期望在另一个翻译单元中使用它而其他翻译单元看不到该声明。
基本上,您链接在一起的所有翻译单元必须具有相同的 class、对象和模板声明。这包括模板专业化。
这只是 C++ templates can only be portably defined in header files.
的迂回说法
查看 tps.hpp 中的 base::m_visible
方法(这是“默认”):
#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
;
}
专门用于 tps_spec.cpp 中的 inheritor
结构(名为 «spec»):
#include "tps.hpp"
template <> void base<inheritor>::m_visible() const
{
std::cout
<< "spec" << std::endl
;
}
void inheritor::log() const
{
m_log();
}
并从 tps_main.cpp 进一步调用:
#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;
}
结果取决于编译单元的顺序 (GCC 4.8.4):
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
这只会在优化 -O3
时发生。任何带有注释标记的实验版本都会产生不同的结果。 为什么?
因为这是未定义的行为。
如果模板是特化的,则这种特化必须"visible"无处不在。
您在一个翻译单元中声明并实施了专业化。那个专门化的模板声明,基本上,完全在那个翻译单元内部,并且专门化只对那个翻译单元可见。您的 tps_main
翻译单元对模板专业化一无所知,也一无所知;因此,将其与具有此模板特化可见的不同翻译单元链接在一起会导致未定义的行为。
你需要在头文件中声明并定义(有一些例外情况在这里不是很相关),所以每个包含带有模板定义的头文件的翻译单元也将具有专业化的定义。
模板特化不是某种模板的 "implementation",用于特定模板实例。模板专业化基本上是一个完全独立的 class 声明。因此,如果您有一个使用 class 的翻译单元,它必须声明 class。
您不能只在一个翻译单元中声明 class,然后期望在另一个翻译单元中使用它而其他翻译单元看不到该声明。
基本上,您链接在一起的所有翻译单元必须具有相同的 class、对象和模板声明。这包括模板专业化。
这只是 C++ templates can only be portably defined in header files.
的迂回说法