static constexpr模板成员在专门化时给出undefined-reference

时间:2017-12-19 11:56:21

标签: c++ templates linker-errors constexpr

以下代码给出了未定义的引用链接错误:

template<int>
struct X {
    static constexpr int x = 0;
};

template<>
constexpr int X<1>::x;

int main() 
{   
    return X<1>::x;
}

但我不确切知道原因。

是否可以定义数据成员而不专门化整个模板?

要明确:此代码编译良好,但提供链接器错误(未定义引用)。

3 个答案:

答案 0 :(得分:4)

由于明确专业化,你偶然发现了一个“小”问题。如果我们引用[temp.expl.spec]/13

  

模板或静态数据成员的显式特化   静态数据成员模板的显式特化是一个   如果声明包含初始化程序,则定义;否则,它   是宣言。 [注意:静态数据成员的定义   需要默认初始化的模板必须使用a   支撑-INIT-列表:

template<> X Q<int>::x;                         // declaration
template<> X Q<int>::x ();                      // error: declares a function
template<> X Q<int>::x { };                     // definition
     

- 结束说明]

意味着您声明 X<1>::x存在,但没有定义它。因此它未定义。

我觉得很疯狂,是你的编译器接受它。一般而言,如果没有定义constexpr变量,则无法声明它们。这很奇怪。

答案 1 :(得分:3)

  

是否可以在没有[专门化]整个模板的情况下定义数据成员?

static类模板的数据成员允许显式专门化([temp.expl.spec]),但是如果您希望这样做,则无法指定类模板(class.static.data)中成员的初始化程序。也就是说,

如果我们将constexpr替换为const,则此代码可以正常使用:

template<int>
struct X {
    static const int x;
};

template<int I>
const int X<I>::x = 0;

template<>
const int X<1>::x = 1;

但是这段代码 NOT 没关系:

template<int>
struct X {
    static const int x = 0;
};

template<>
const int X<1>::x = 1;

您可以看到区别在于我们初始化主模板的变量。

现在,如果我们希望将const替换为constexpr,那么我们必需来提供初始值设定项(class.static.data):

  

可以在。中声明文字类型的static数据成员   使用constexpr说明符的类定义;如果是这样,其声明应指定大括号或等于初始值   其中作为赋值表达式的每个 initializer-clause 都是一个常量表达式

因此,我们最终会出现这种奇怪的情况:我们可以专注于static成员,但如果constexpr成员constexpr,则template<int> struct X_Traits{ static constexpr int value = 0; }; template<> struct X_Traits<1>{ static constexpr int value = 1; }; template<int I> struct X { static constexpr int x=X_Traits<I>::value; // ... }; 需要初始化程序。恕我直言,这是标准的缺点。

然而,似乎所有现代编译器都不同意。

gcc 8.0.0按原样编译(但不链接)你的代码(错误),但是如果为专业化添加初始化程序,它会抱怨重复初始化(右)。

clang 6.0.0不按原样(右)编译代码,但是当你添加初始化程序时,它可以毫无障碍地工作(错误,但这可能是标准应该规定的)

MSVC 19.00.23506不按原样编译代码(右),并且在添加初始化程序时没有编译代码(抱怨重新定义)(右)。

最后,将专业化推入辅助Traits类可能更容易:

template<int I>
struct X_Traits{
    static constexpr int get_value(){
        if constexpr(I==1){
            return 1;
        }else{
            return 0;
        }
    }
};

template<int I>
struct X {
    static constexpr int x=X_Traits<I>::get_value();
    // ...
};

int main(){
    static_assert(X<0>::x == 0);
    static_assert(X<1>::x == 1);
}

在C ++ 17及更高版本中,我们可以使用constexpr if来避免需要专门化我们的特征类:

{{1}}

答案 2 :(得分:2)

像这样。

template<int i>
struct X {
    static constexpr int x = i==0?2:10;
};

int main() 
{   
    return X<1>::x;
}

请注意,您不需要在课外定义它。