我读了一些标准库的CLang实现,它让我对const和constexpr感到困惑。
template<class _Tp, _Tp __v>
struct integral_constant
{
static constexpr _Tp value = __v;
};
template<class _Tp, _Tp __v>
const _Tp integral_constant<_Tp, __v>::value;
令我困惑的是,它在类定义和const外部使用constexpr。我的问题是,是允许的吗?在什么情况下const和constexpr可以互换使用?当然constexpr函数不能应用于const,所以我说的是const数据和constexpr数据。
我确实读过一些标准草案和提案 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf, 但它让我感到更加困惑。所以我还有一些问题,
在N2235中,它明确指出,const数据不保证是编译时常量,参见下面的例子,
struct S {
static const int size;
};
const int limit = 2 * S::size; // dynamic initialization
const int S::size = 256;
和constexpr应该解决这个问题,所以至少在这种情况下,不允许使用constexpr,
struct S {
static const int size;
};
constexpr int limit = 2 * S::size; // shall be error in my understanding
const int S::size = 256;
但是,在阅读了C ++标准草案N3225之后,我看到没有明确说明上述示例会导致错误。特别是,从7.1.5 / 9,
一个constexpr说明符 对象声明声明对象 作为常数。这样的对象应该有 字面类型,应初始化。 如果它是由构造函数初始化的 打电话,构造函数应该是 constexpr构造函数和每一个 构造函数的参数应为a 不断表达。那个电话应该 是一个常数表达式(5.19)。 否则,每一个完整的表达 在其初始化器中出现的应为a 不断表达。
因此,如果constexpr int limit = 2 * S :: size;是无效的,那么S :: size不能是一个常量表达式,那么从5.19(常量表达式),我看不到上面例子中的标准禁止2 * S :: size不是常量表达式。
有人能指出我忽略的任何事情吗?非常感谢你。
答案 0 :(得分:4)
根据N3225§5.19p2:
,S :: size不是常量表达式条件表达式是一个常量表达式,除非它涉及以下之一...
- 左值 - 右值转换(4.1),除非适用于
- 整数或枚举类型的glvalue,它引用具有前面初始化的非易失性const对象,用常量表达式初始化,或
- [其他不适用的条件]
注意我引用的第二个项目符号点是如何允许一个整数静态数据成员,它本身用常量表达式初始化也是一个常量表达式,但你的S :: size是未初始化的。
(旁注:常量表达式是根据条件表达式定义的,因为这就是C ++语法的工作原理。)
如果您想知道左值到右值的转换是如何发生的,请参阅§5p9:
每当glvalue表达式作为操作符的操作数出现时,该操作符需要该操作数的prvalue,lvalue-to-rvalue(4.1),array-to-pointer(4.2)或function-to-pointer(4.3)应用标准转换以将表达式转换为prvalue。
这可能是阅读标准doesn't make a good reference的一个很好的例子,尽管还没有其他的可用于0x。
答案 1 :(得分:2)
“它的初始化程序中出现的每个完整表达式都应该是一个常量表达式”
S::size
不是常量表达式,因此它不能出现在常量表达式的初始化中。