【发布时间】:2011-08-25 12:34:28
【问题描述】:
我正在研究 C++ 链接器在模板特化方面的行为。我正在使用 Microsoft Visual C++ 2010 进行这些实验。我不知道其他工具链(例如 gcc)的行为是否相同。
这是第一个代码sn-p:
// 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.
让我们通过将foo<double>() 的特化声明为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 关键字:匿名命名空间不会这样做,因为我们不能将模板函数专门化到与其声明不同的命名空间中。)
现在,static 关键字的使用相当不直观。通常,特化 foo<double>() 将驻留在头文件中的某个位置,因此将被标记为内联,如以下 sn-p:
// 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是什么意思?
最后一个sn-p:
// 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 中实例化(虽然声明还在),所以唯一剩下的实例化就是 main.cpp 中的那个em>bar.cpp.
【问题讨论】:
-
在您的第三段中,bar.cpp 中的调用不是“隐式专业化”,而是“实例化”,因为您只 调用
foo<double>()(您是不专攻任何东西)。此外,下一句,“显式专业化”可能应该只是“专业化”或“完全专业化”,以避免与听起来相似的“显式实例化”(您没有)混淆。 -
我想如果你
inline一个函数,你保证它在所有翻译单元中都有相同的定义。如果这是真的,那么您将招致未定义的行为。 -
@Kerrek 实际上这不是真的。允许内联函数在不同的翻译单元中有不同的实现。它们是单一定义规则的一个例外。
-
@Let_Me_Be:不,他们不是。此类事物的每个定义都必须包含相同的标记序列,否则您将有未定义的行为。当违反单一定义规则的某些方面时,工具链不需要给出任何诊断。
-
@Let_Me_Be:内联函数不能免除单一定义规则。函数模板是,但不是(完整的)函数模板特化。
标签: c++ templates linker inline specialization