了解静态constexpr成员变量

时间:2015-12-02 21:43:50

标签: c++ c++11 constexpr

我对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的行为也相同。

1 个答案:

答案 0 :(得分:13)

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

  

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

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

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