【发布时间】:2012-03-08 07:24:38
【问题描述】:
模板声明与模板定义的匹配程度如何?我在标准中发现了一些关于 template-ids 引用相同函数的文本,如果“他们的 template-names [...] 引用相同的模板并且 [.. .]" (14.4 [temp.type] p1) 但我找不到 template-names 的定义或当 template-names 引用同一模板时。我不确定我是否走在正确的轨道上,因为我还没有很好地破译语法以判断 template-id 是否是模板定义/声明的一部分,或者只是一个模板的使用。
例如,下面的程序可以正常工作。
#include <iostream>
template<typename T>
T foo(T t);
int main() {
foo(1);
}
template<typename T>
T foo(T t)
{ std::cout << "A\n"; return 0; }
如果我更改在模板定义中使用模板参数的方式,名称显然不再引用同一个模板,并且链接失败。
#include <iostream>
template<typename T>
T foo(T t);
int main() {
foo(1);
}
template<typename T>
int foo(T t) { std::cout << "A\n"; return 0; }
// or
template<typename T>
struct identity {
typedef T type;
};
template<typename T>
typename identity<T>::type
foo(T t) { std::cout << "A\n"; return 0; }
接下来,如果我将模板定义移动到另一个翻译单元,对于我的 C++(MSVC 11 测试版)实现,无论我如何说类型,程序都能正常工作。
//main.cpp
template<typename T>
T foo(T t);
int main() {
foo(1);
}
//definition.cpp
#include <iostream>
template<typename T>
struct identity {
typedef T type;
};
template<typename T>
typename identity<T>::type
foo(T t) { std::cout << "A\n"; return 0; }
template int foo<int>(int);
或
//definition.cpp
#include <iostream>
template<typename T>
int foo(T t) { std::cout << "A\n"; return 0; }
template int foo<int>(int);
或者即使定义根本不是模板:
//definition.cpp
#include <iostream>
int foo(T t) { std::cout << "A\n"; return 0; }
显然链接是成功的,因为无论为创建符号而实例化的模板如何,签名/损坏的名称都是相同的。我认为这种未定义的行为是因为我违反了:
§ 14.1 [临时] p6
函数模板、类模板的成员函数或静态 类模板的数据成员应在每个翻译中定义 它被隐式实例化的单元(14.7.1),除非 相应的特化在 (14.7.2) 中显式实例化 一些翻译单元;不需要诊断。
然后说我试图通过将模板的定义放在第二个翻译单元中来满足这些要求,并在两个位置之一包含一个显式实例化:
#include <iostream>
template<typename T>
T foo(T t) { std::cout << "A\n"; return 0; }
// Location 1
template<typename T>
int foo(int t) { std::cout << "B\n"; return 0; }
// Location 2
消除显式实例化所指模板的歧义的规则是什么?将其放在位置 1 会导致正确的模板被实例化,并且该定义将在最终程序中使用,而将其放在位置 2 会实例化另一个模板,并导致我认为在上述 14.1 p6 下的未定义行为。
另一方面,两个模板定义的隐式实例化无论如何都会选择第一个模板,因此在这些情况下,消除模板歧义的规则似乎有所不同:
#include <iostream>
template<typename T>
T foo(T t) { std::cout << "A\n"; return 0; }
template<typename T>
int foo(int t) { std::cout << "B\n"; return 0; }
int main() {
foo(1); // prints "A"
}
出现此问题的原因与this question 有关,提问者在其中发现单个前向声明
template<typename T>
T CastScriptVarConst(const ScriptVar_t& s);
不能作为多个模板定义的声明:
template<typename T>
typename std::enable_if<GetType<T>::value < SVT_BASEOBJ,T>::type
CastScriptVarConst(const ScriptVar_t& s) {
return (T) s;
}
template<typename T>
typename std::enable_if<!(GetType<T>::value < SVT_BASEOBJ)
&& std::is_base_of<CustomVar,T>::value,T>::type
CastScriptVarConst(const ScriptVar_t& s) {
return *s.as<T>();
}
我想更好地理解模板定义和声明之间的关系。
【问题讨论】:
-
为完整的故事挑选一份“C++ 模板:完整指南”。是的,它比标准更容易消化......实际上很多;)