简体   繁体   English

在C ++ 11中将局部变量的地址作为常量表达式吗?

[英]Is taking the address of a local variable a constant expression in C++11?

The following C++11 program: 以下C ++ 11程序:

int x = 42;

void f()
{
        int y = 43;

        static_assert(&x < &y, "foo");
}

int main()
{
        f();
}

Doesn't compile with gcc 4.7 as it complains: 不抱怨使用gcc 4.7进行编译:

error: ‘&y’ is not a constant expression

This would agree with my intuition. 这符合我的直觉。 The address of y potentially changes with each invocation of f , so of course it cannot be calculated during translation. y的地址可能随f每次调用而改变,因此当然在翻译期间无法计算。

However none of the bullet points in 5.19 [expr.const] seem to preclude it from being a constant expression. 然而,5.19 [expr.const]中没有一个要点似乎排除了它作为一个常量表达式。

The only two contenders I see are: 我看到的唯一两个竞争者是:

an lvalue-to-rvalue conversion... 左值到左值的转换......

but unless I am mistaken (?) there are no lvalue-to-rvalue conversions in the program. 但除非我弄错了(?),否则程序中没有左值到右值的转换。

And

an id-expression that refers to a variable [snip] unless: 一个引用变量[snip]的id-expression ,除非:

  • it is initialized with a constant expression 它用一个常量表达式初始化

which y is - it is initialized with the constant expression 43 . 其中y是 - 用常量表达式43初始化。

So is this an error in the standard, or am I missing something? 那么这是标准中的错误,还是我错过了什么?

Update: 更新:

It's confusing as hell, but I think I am on top of it, so let me show an example that will show off what is going on: 令人困惑的是地狱,但我认为我处于最重要的位置,所以让我展示一个展示正在发生的事情的例子:

int x = 42;

void f()
{
        int y = 43;

        // address constant expressions:    
        constexpr int* px = &x; // OK
        constexpr int* py = &y; // ERROR: pointer context for local variable

        // boolean constant expressions:
        constexpr bool bx = &x; // OK
        constexpr bool by = &y; // OK

        // comparison constant expressions:
        constexpr bool eq = (&x == &y); // OK
        constexpr bool lt = (&x < &y); // ERROR: undefined behaviour disqualifies 
                                                 a constant expression
}

int main()
{
        f();
}

First distinguish between a core constant expression (5.19p2) and a constant expression (5.19p4). 首先区分核心常数表达式(5.19p2)和常量表达式(5.19p4)。 Specifcally sub-expressions of a constant expression only have to be core constant expressions, not constant expressions. 常量表达式的特定子表达式只需要是核心常量表达式,而不是常量表达式。 That is, being a constant expression is a property of the full expression, not sub-expressions. 也就是说,作为常量表达式是完整表达式的属性,而不是子表达式。 It further requires to look at the context it which the full expression is used. 它还需要查看使用完整表达式的上下文。

So, as it turns out the gcc error is misleading. 所以,事实证明gcc错误是误导性的。 Firstly &y may be a constant expression in some contexts. 首先&y在某些情况下&y可能是一个常数表达式。 Secondly, the reason &x < &y isn't a constant expression is because of the comparison of unrelated pointers, not of the sub-expression &y . 其次,原因&x < &y不是常量表达式是因为不相关指针的比较,而不是子表达式&y

Let's try to determine which requirements the expression in the static_assert-declaration has to fulfil step-by-step, using n3485. 让我们尝试使用n3485确定static_assert-declaration中表达式必须逐步完成的要求。

[dcl.dcl]/1 [dcl.dcl] / 1

static_assert-declaration: static_assert声明:
static_assert ( constant-expression , string-literal ) ; static_assert ( constant-expression , string-literal ) ;

[dcl.dcl]/4 [dcl.dcl / 4

In a static_assert-declaration the constant-expression shall be a constant expression that can be contextually converted to bool . static_assert声明中constant-expression应该是一个常量表达式,可以在上下文中转换为bool

[expr.const]/4 [expr.const / 4

Collectively, literal constant expressions , reference constant expressions , and address constant expressions are called constant expressions . 文字常量表达式引用常量表达式地址常量表达式统称为常量表达式


So what type of constant expression is &x < &y ? 那么什么类型的常量表达式是&x < &y It is not an address constant expression : 不是 地址常量表达式

[expr.const]/4 [expr.const / 4

An address constant expression is a prvalue core constant expression (after conversions as required by the context) of type std::nullptr_t or of pointer type [...]. 地址常量表达式是类型为std::nullptr_t或指针类型[...]的prvalue核心常量表达式(在上下文要求的转换之后)。

The type of &x < &y is bool as per [expr.rel]/1. &x < &y的类型是[expr.rel] / 1的bool

It isn't a reference constant expression either, so it must be a literal constant expression , if any . 它也不是引用常量表达式 ,因此它必须是文字常量表达式如果有的话)

A literal constant expression is a prvalue core constant expression of literal type [...] 文字常量表达式是文字类型的prvalue核心常量表达式[...]

Therefore, &x < &y has to fulfil the requirements of a core constant expression . 因此, &x < &y必须满足核心常量表达式的要求。


As pointed out by TemplateRex and hvd in the comments, in this particular case, &x < &y does not fulfil the requirements of a core constant expression : 正如评论中的TemplateRexhvd指出的那样 ,在这种特殊情况下, &x < &y不满足核心常量表达式的要求:

[expr.const]/2 [expr.const] / 2

[a core constant expression must not contain] a relational or equality operator where the result is unspecified; [核心常量表达式不得包含]关系或相等运算符,其中结果未指定;

[expr.rel]/2 [expr.rel] / 2

If two pointers p and q of the same type point to different objects that are not members of the same object or elements of the same array or to different functions, or if only one of them is null, the results of p<q , p>q , p<=q , and p>=q are unspecified. 如果相同类型的两个指针pq指向不是同一对象的成员或同一数组的元素或不同函数的不同对象,或者如果只有其中一个为null,则p<qp>qp<=qp>=q未指定。

However, for an example like 但是,举个例子

int arr[2] = {1, 2};
static_assert(&a[0] < &a[1], "");

The expression a < a+1 fulfils this requirement as well. 表达式a < a+1满足此要求。

Yes, you're missing the fact that, while y itself is initialised with a constant expression, that's not the same as &y . 是的,你错过了一个事实,即,当y本身与常量表达式初始化,这是一样的&y

The address of y can vary a great deal depending on your call stack history. 根据您的调用堆栈历史记录, y的地址可能会有很大差异。

Paragraph 3 of C++11 5.19 Constant expressions states the conditions under which the address-of operator can be considered a constant expression (how core constant expressions detailed in paragraph 2 are allowed to morph into "real" constant expressions): C++11 5.19 Constant expressions第3段C++11 5.19 Constant expressions说明了可以将address-of运算符视为常量表达式的条件(允许将第2段中详述的核心常量表达式转换为“实数”常量表达式):

... An address constant expression is a prvalue core constant expression of pointer type that evaluates to the address of an object with static storage duration, to the address of a function, or to a null pointer value, or a prvalue core constant expression of type std::nullptr_t. ...地址常量表达式是指针类型的prvalue核心常量表达式,它计算具有静态存储持续时间的对象 的地址,函数的地址,空指针值,prvalue核心常量表达式输入std :: nullptr_t。 Collectively, literal constant expressions, reference constant expressions, and address constant expressions are called constant expressions. 文字常量表达式,引用常量表达式和地址常量表达式统称为常量表达式。

Since &y is none of those things, it's not considered a constant expression. 因为&y不是那些东西,所以它不被认为是一个恒定的表达式。

Taking the address of something is not the culprit here, but rather the pointer comparison using operator< on unrelated objects. 取一些东西的地址不是这里的罪魁祸首,而是使用operator<对无关对象的指针比较。

Relational operators on pointers is only specified for pointers to objects within the same class or within an array (5.9 Relational operators [expr.rel], points 3 and 4.) Relational comparison for pointers to unrelated objects is unspecified. 指针上的关系运算符仅指定指向同一类或数组内对象的指针(5.9关系运算符[expr.rel],第3点和第4点。) 未指定指向不相关对象的指针的关系比较。

Comparing the address for equality rather than ordering does work: 比较地址的相等而不是排序确实有效:

int x = 42;

void f()
{
        int y = 43;

        static_assert(&x != &y, "foo");
                         ^^ <--- "<" on unrelated objects is unspecified
}

int main()
{
        f();
}

Live example 实例

Just to show that this has nothing to do with const-expressions per se, 只是为了表明这与const表达式本身无关,

void f()
{
        int y[2] = { 42, 43 };

        static_assert(&y[0] < &y[1], "foo");
                            ^ <--- "<" on objects within an array is specified
}

int main()
{
        f();
}

Another live example . 另一个实例

Sorry, I agree that the previous answer was probably an incorrect reading of the items. 对不起,我同意以前的回答可能是错误的阅读项目。 Instead, the actual relevant clause is 5.19 [expr.const] paragraph 3 which reads (highlightening added): 相反,实际相关条款是5.19 [expr.const]第3段,其中包括(突出显示):

A literal constant expression is a prvalue core constant expression of literal type, but not pointer type . 文字常量表达式是文字类型的prvalue核心常量表达式, 但不是指针类型 An integral constant expression is a literal constant expression of integral or unscoped enumeration type. 整数常量表达式是整数或未整数枚举类型的文字常量表达式。 [ Note: Such expressions may be used as array bounds (8.3.4, 5.3.4), as bit-field lengths (9.6), as enumerator initializers if the underlying type is not fixed (7.2), as null pointer constants (4.10), and as alignments (7.6.2). [注意:这些表达式可以用作数组边界(8.3.4,5.3.4),作为位字段长度(9.6),作为枚举器初始化器,如果基础类型不固定(7.2),作为空指针常量(4.10) )和作为比对(7.6.2)。 —end note ] A converted constant expression of type T is a literal constant expression, implicitly converted to type T, where the implicit conversion (if any) is permitted in a literal constant expression and the implicit conversion sequence contains only user-defined conversions, lvalue-to-rvalue conversions (4.1), integral promotions (4.5), and integral conversions (4.7) other than narrowing conversions (8.5.4). -end note] T类型的转换常量表达式是一个文字常量表达式,隐式转换为类型T,其中在文字常量表达式中允许隐式转换(如果有),隐式转换序列仅包含用户定义的转换,左值转换(4.1),整数促销(4.5)和积分转换(4.7),而不是缩小转化次数(8.5.4)。 [ Note: such expressions may be used as case expressions (6.4.2), as enumerator initializers if the underlying type is fixed (7.2), and as integral or enumeration non-type template arguments (14.3). [注意:此类表达式可用作案例表达式(6.4.2),如果基础类型是固定的(7.2),则作为枚举器初始值设定项,以及作为整数或枚举非类型模板参数(14.3)。 —end note ] A reference constant expression is an lvalue core constant expression that designates an object with static storage duration or a function. -end note]引用常量表达式是一个左值核心常量表达式,用于指定具有静态存储持续时间或函数的对象。 An address constant expression is a prvalue core constant expression of pointer type that evaluates to the address of an object with static storage duration , to the address of a function, or to a null pointer value, or a prvalue core constant expression of type std::nullptr_t. 地址常量表达式是指针类型的prvalue核心常量表达式,其计算为具有静态存储持续时间的对象的地址,函数的地址,或空指针值,或std类型的prvalue核心常量表达式: :nullptr_t。 Collectively, literal constant expressions, reference constant expressions, and address constant expressions are called constant expressions. 文字常量表达式,引用常量表达式和地址常量表达式统称为常量表达式。

A core constant expression isn't directly a constant expression, there are additional conditions which are spelled out in this third paragraph. 核心常量表达式不是直接表达式,在第三段中列出了附加条件。

5.19p2 does not define constant expressions, it defines core constant expressions. 5.19p2没有定义常量表达式,它定义了核心常量表达式。

A core constant expression only becomes a constant expression if it conforms to one of the rules in 5.19p3. 如果核心常量表达式符合5.19p3中的规则之一,则它仅变为常量表达式 There, the relevant part was already pointed out by jrok: 在那里,jrok已经指出了相关部分:

An address constant expression is a prvalue core constant expression of pointer type that evaluates to the address of an object with static storage duration, to the address of a function, or to a null pointer value, or a prvalue core constant expression of type std::nullptr_t . 地址常量表达式是指针类型的prvalue核心常量表达式,其计算为具有静态存储持续时间的对象的地址,函数的地址,或空指针值,或std::nullptr_t类型的prvalue核心常量表达式std::nullptr_t

Your core constant expression &y does not evaluate to any of those, so it's not an address constant expression, and thus not a constant expression. 你的核心常量表达式&y不会评估任何这些,因此它不是地址常量表达式,因此不是常量表达式。

In C++ pre-C++11: 在C ++ pre-C ++ 11中:

Other expressions [than integral constant expressions] are considered constantexpressions only for the purpose of nonlocal static object initialization (3.6.2). 除了非本地静态对象初始化(3.6.2)之外,其他表达式[比积分常量表达式]被认为是constantexpressions。 Such constant expressions shall evaluate to one of the following: 这些常量表达式应评估为以下之一:

[...] [...]

-- an address constant expression, - 地址常量表达式,

[...] [...]

An address constant expression is a pointer to an lvalue designating an object of static storage duration, a string literal (2.13.4), or a function. 地址常量表达式是指向左值的指针,该左值指定静态存储持续时间的对象,字符串文字(2.13.4)或函数。

Since y doesn't have static storage duration, &y would not be a constant expression. 由于y没有静态存储持续时间,因此&y不会是常量表达式。

C++11 seems to have changed this; C ++ 11似乎改变了这一点; I suspect that this is an oversignt, however. 但我怀疑这是一种过度的行为。 (C++ pre-C++11 lists the things that are constant expressions. C++11 lists the things that aren't. It would be easy for one to have been forgotten.) (C ++ pre-C ++ 11列出了常量表达式的东西.C ++ 11列出了没有的东西。人们很容易被遗忘。)

Regardless, of course: your comparison isn't usable in standard C++; 当然,无论如何:您的比较在标准C ++中无法使用; the results of comparing two addresses which don't point into the same object is unspecified. 未指定比较两个未指向同一对象的地址的结果。 (On the other hand, I have occasionally used something similar in machine dependent code. Not statically, but on platforms like Linux on PC or Solaris, it's possible to determine whether a pointer points to an object with static lifetime, and auto variable, or dynamically allocated memory with such tricks.) (另一方面,我偶尔会在机器相关代码中使用类似的东西。不是静态的,但在PC或Solaris上的Linux平台上,可以确定指针是否指向具有静态生命周期和自动变量的对象,或者用这些技巧动态分配内存。)

EDIT: 编辑:

The answser by paxdiablo has quoted the passage I didn't find in my reading of C++11; paxdiablo的回答引用了我在阅读C ++ 11时没有找到的段落; C++11 follows the same rule as C++ pre-11 in this respect, and in order to be a constant address expression, the address must be that of an object with static lifetime (or a function, or a null pointer). 在这方面,C ++ 11遵循与C ++ pre-11相同的规则,并且为了成为常量地址表达式,地址必须是具有静态生存期(或函数或空指针)的对象的地址。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM