了解静态 constexpr 成员变量

Understanding static constexpr member variables

我对 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 中,我有:

在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):

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (5.20). A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. — end note ] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.

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

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