对于此代码,我无法从不同的编译器中获得一致的行为,并且正在尝试弄清楚我可能做错了什么。基本设置有一个模板类(打印机),一个部分特化(用于奇数整数),它添加了一个静态常量成员。当我尝试以各种不同的方式定义静态常量成员时,问题就出现了。
我已将代码缩减为三个文件。为了回答一个直接的问题,我避免使用 c++11,所以我提供了一个自定义的 enable_if 模板。
文件“template_bug.h”
template<bool B, class T = void> struct my_enable_if {};
template< class T > struct my_enable_if<true, T> { typedef T type; };
template< int Value, typename = void > struct Printer {
static void doIt() { std::cout << "Printer<" << Value << "(even)>::doIt()" << std::endl; }
};
template< int Value > struct Printer< Value, typename my_enable_if<Value & 1>::type > {
static const char *c_prefix;
static void doIt() { std::cout << "Printer<" << c_prefix << Value << "(odd)>::doIt()" << std::endl; }
};
template<> const char *Printer<1>::c_prefix = "One_";
文件“other.cc”:
#include <iostream>
#include "template_bug.h"
template<> const char *Printer<5>::c_prefix = "Five_";
文件“main.cc”:
#include <iostream>
#include "template_bug.h"
template<> const char *Printer<3>::c_prefix = "Three_";
int main(void)
{
Printer<1>::doIt();
Printer<3>::doIt();
Printer<5>::doIt();
return 0;
}
现在,我的理解是这段代码应该编译并且不会调用任何未定义的行为,但我觉得我一定是错了。当我在 g++ (4.8.4) 中编译它时:
g++ -c -o main.cc.o main.cc
g++ -c -o other.cc.o other.cc
g++ -o template_bug main.cc.o other.cc.o
我在链接阶段得到这个:
other.cc.o:(.data+0x0): multiple definition of `Printer<1, void>::c_prefix'
main.cc.o:(.data+0x0): first defined here
我的印象是这个符号应该有专门的弱链接以避免这个错误。
问题1:为什么是Printer<1>::c_prefix
不是弱符号(在 g++ 下)?
如果我注释掉 Printer<1>
用法,其余部分按预期编译、链接和执行。
在不同的编译器 (Greenhills) 上,我得到的错误是:
[elxr] (error #412) unresolved symbols: 1
Printer<N1, my_enable_if<(bool)(N1&((int)1)), void>::type>::c_prefix [with N1=(int)5] from main.o
根本原因是编译器选择在两个编译单元之间生成不同的符号名称。使用 nm 我可以看到差异:
来自 nm other.o:
00000000 D c_prefix__S__94Printer__ps__63_XZ1ZQ2_48my_enable_if__tm__28_XOcsb_1_Oad_2_Z1ZCiL_1_1OOv4type__tm__9_XCiL_1_5
来自 nm main.o:
U c_prefix__94Printer__ps__63_XZ1ZQ2_48my_enable_if__tm__28_XOcsb_1_Oad_2_Z1ZCiL_1_1OOv4type__tm__9_XCiL_1_5
在与他们的支持人员交换了几封电子邮件后,我被告知我的代码正在调用 UB,但他们对为什么这是 UB 的解释没有意义。
问题 2:与 Printer<5>
有关吗?调用未定义行为的用法?
最佳答案
Question 2: Is anything about the
Printer<5>
usage invoking undefined behavior?
必须在使用前声明特化,否则程序格式错误(无需诊断)。
所以正确的方法是:
文件“template_bug.h”
template<bool B, class T = void> struct my_enable_if {};
template< class T > struct my_enable_if<true, T> { typedef T type; };
template< int Value, typename = void > struct Printer {
static void doIt() {
std::cout << "Printer<" << Value << "(even)>::doIt()" << std::endl;
}
};
template< int Value > struct Printer< Value, typename my_enable_if<Value & 1>::type > {
static const char *c_prefix;
static void doIt() {
std::cout << "Printer<" << c_prefix << Value << "(odd)>::doIt()" << std::endl;
}
};
template<> const char *Printer<1>::c_prefix; // Declaration
template<> const char *Printer<3>::c_prefix; // Declaration
// or since C++17, inline static variable.
template<> inline const char *Printer<5>::c_prefix = "Five_";
在一个cpp中(可能会被拆分成几个cpp,但只有一个定义):
template<> const char *Printer<1>::c_prefix = "One_"; // Definition
template<> const char *Printer<3>::c_prefix = "Three_"; // Definition
关于c++ - 模板部分特化和多个编译单元,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49439113/