模板专业化的 ODR 违规

ODR violation with template specializations

我们有一个 header 文件,其中包含各种浮点精度的一些残差:

template <typename T>
struct rsdTarget {
  static const double value;
};

template <>
const double rsdTarget<half>::value = (double)(1.0e-3);

template <>
const double rsdTarget<float>::value = (double)(1.0e-7);

template <>
const double rsdTarget<double>::value = (double)(1.0e-12);

之所以有效,是因为 header 只包含在一个编译单元中。现在,我尝试在多个编译单元中使用此 header,但我收到了源于 ODR 的链接器错误:

CMakeFiles/tests_g.dir/random_gauge.cc.o:(.rodata+0x108): multiple definition of `rsdTarget<double>::value'
CMakeFiles/tests_g.dir/clover_product.cc.o:(.rodata+0x548): first defined here

初始化可能需要进入源文件并从 header 文件中取出。但是,似乎禁止在 const double.

前面添加一个 extern

我需要做什么才能使它适用于多个编译单元?

更新

我认为为 double 解决这个问题会完全解决它。然而,还有第二种 non-literal 类型,我也必须通过:

template <typename T>
struct tolerance {
  static const QDP::Double small; // Always fail
};

template <>
const QDP::Double tolerance<half>::small = QDP::Double(5.0e-3);

template <>
const QDP::Double tolerance<float>::small = QDP::Double(1.0e-6);

template <>
const QDP::Double tolerance<double>::small = QDP::Double(1.0e-7);

我似乎无法将其与 constexpr 一起使用,因为该类型不支持它(需要 constexpr ctor,对吧?)。哪个解决方案也适用于此?

您可以允许所有这些定义驻留在头文件中,而不会在多个翻译单元中使用时导致链接器错误,您只需要将它们也转换为模板:

template<typename T, typename TDummy = void>
struct rsdTarget;

template<typename TDummy>
struct rsdTarget<half, TDummy>
{
    static const double value;
};

template<typename TDummy>
const double rsdTarget<half, TDummy>::value = (double)(1.0e-3);

template<typename TDummy>
struct rsdTarget<float, TDummy>
{
    static const double value;
};

template<typename TDummy>
const double rsdTarget<float, TDummy>::value = (double)(1.0e-7);

template<typename TDummy>
struct rsdTarget<double, TDummy>
{
    static const double value;
};

template<typename TDummy>
const double rsdTarget<double, TDummy>::value = (double)(1.0e-12);

之前的 C++1z

template<typename>
struct rsdTarget;

template<>
struct rsdTarget<half>
{
    static constexpr double value = 1e-3;
};

// and so on...

这有一个限制,即 value 不能被 odr-使用,但在大多数情况下,这对常量来说不是问题。

Post C++1z,你可以只使用 inline 变量并避免整个 odr 惨败。