了解静态 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.2
和 clang-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。因此,正如上面的措辞所规定的,您必须提供一个定义。
现在您已经发现基本类型是按值传递的,这就是为什么不需要定义的原因。
我对 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.2
和 clang-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。因此,正如上面的措辞所规定的,您必须提供一个定义。
现在您已经发现基本类型是按值传递的,这就是为什么不需要定义的原因。