静态 constexpr 模板成员在专门化时给出未定义的引用

wim*_*aan 5 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;
}
Run Code Online (Sandbox Code Playgroud)

但我不知道确切的原因。

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

需要明确的是:这段代码编译得很好,但会出现链接器错误(未定义引用)。

And*_*dyG 5

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

static类模板的数据成员允许明确的专业([temp.expl.spec]),但是如果你想做到这一点,你已经不能指定类模板中的成员的初始化(class.static.data) . 那是,

如果我们替换constexprconst,这段代码就可以了:

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

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

template<>
const int X<1>::x = 1;
Run Code Online (Sandbox Code Playgroud)

但是这段代码不会很好:

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

template<>
const int X<1>::x = 1;
Run Code Online (Sandbox Code Playgroud)

您可以看到不同之处在于我们初始化主模板的变量的位置。

现在,如果我们希望替换constconstexpr,那么我们需要提供一个初始化程序(class.static.data):

static文字类型的数据成员可以在类定义与声明constexpr说明符; 如果是这样,它的声明应指定一个大括号或等号初始化器 ,其中每个作为赋值表达式的初始化器子句都是一个常量表达式

所以我们最终会遇到这种奇怪的情况,我们可以专门化static成员,但如果constexpr因为constexpr需要初始化程序则不行。恕我直言,这是标准的一个缺点。

但是,似乎并非所有现代编译器都同意。

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

clang 6.0.0 没有按原样编译代码(正确),但是当您添加初始化程序时,它可以顺利运行(错误,但这可能是标准应该规定的)

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

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

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;
    // ...
};
Run Code Online (Sandbox Code Playgroud)

在 C++17 及更高版本中,我们可以使用constexpr if来避免需要专门化我们的 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);
}
Run Code Online (Sandbox Code Playgroud)