了解静态constexpr成员变量

Understanding static constexpr member variables

本文关键字:变量 成员 constexpr 静态 了解      更新时间:2023-10-16

我对C++11中的static constexpr成员变量有一些混淆。

在first.hpp

template<typename T>
struct cond_I
{ static constexpr T value = 0; }; 

// specialization 
template<typename T>
struct cond_I< std::complex<T> >
{ static constexpr std::complex<T> value = {0,1}; }; 

在main()函数中

cout << cond_I<double>::value << endl;            // this works fine
cout << cond_I< complex<double> >::value << endl; // linker error

但是,如果我在first.hpp中添加以下行,一切都会正常工作。

template<typename T1> 
constexpr std::complex<T1> cond_I< std::complex<T1> >::value;

我所理解的(我可能错了)是,cond_I< std::complex<double> >::value需要一个定义,但在前面的情况下,它只有声明。但是cond_I<double>::value呢?为什么它不需要定义?

同样,在另一个头文件second.hpp中,我有:

// empty struct
template<typename T>
struct eps
{ };

// special cases
template<>
struct eps<double>
{
  static constexpr double value = 1.0e-12;
};
template<>
struct eps<float>
{
  static constexpr float value = 1.0e-6;
};

在这种情况下,以下代码在没有任何eps<>::value定义的情况下可以完美工作。

在main()函数中

cout << eps<double>::value << endl;    //  works fine
cout << eps<float>::value << endl;     //  works fine

有人能解释一下static constexpr成员变量在这些场景中的不同行为吗?

这些行为对于gcc-5.2clang-3.6也是相同的。

根据标准9.4.2/p3静态数据成员[class.Static.data]Emphasis Mine):

如果非易失性const静态数据成员是整型或枚举型,则其在类定义中的声明可以指定大括号或等号初始化器,其中作为赋值表达式的每个初始化器子句都是常量表达式(5.20)。文本类型的静态数据成员可以在具有constexpr说明符的类定义中声明;如果是这样的话,它的声明应该指定一个大括号或相等的初始值设定项,其中作为赋值表达式的每个初始值设定子句都是一个常量表达式。[注:在两者中在这些情况下,成员可能出现在常量表达式中。——结束注释]如果在程序中使用odr(3.2),则成员仍应在名称空间范围中定义,并且名称空间范围定义不应包含初始值设定项

正如M.M之前在评论ostream::operator<<(ostream&, const complex<T>&)中所解释的那样,该值被认为是程序中使用的odr。因此,根据上述措辞,您必须提供一个定义。

现在,您已经发现基本类型是通过值传递的,这就是为什么不需要定义的原因。