c++ - 模板部分特化和多个编译单元

标签 c++ template-specialization

对于此代码,我无法从不同的编译器中获得一致的行为,并且正在尝试弄清楚我可能做错了什么。基本设置有一个模板类(打印机),一个部分特化(用于奇数整数),它添加了一个静态常量成员。当我尝试以各种不同的方式定义静态常量成员时,问题就出现了。

我已将代码缩减为三个文件。为了回答一个直接的问题,我避免使用 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/

相关文章:

c++ - 计算两幅图像的均方误差

c++ - 我如何处理无效的异常处理例程?

c++ - 基于静态成员类型的模板函数特化

c++ - 编译器错误 C2766: "explicit specialization; ' specialization' has already been defined"when using boost::disable_if

c++ - 如何将 enable_if 用于模板类成员的外联定义

c++ using-declarations 用于从基类调用函数

c++ - g++ 与 DevIL : unable to link

c++ - 当构造函数抛出异常时删除运算符段错误

c++ - 这是什么构造 : template <int> void funcName(int i)?

c++ - 编译模板函数时形式参数列表不匹配