为什么链接器不会在下面的代码中发出错误?
Why doesn't the linker emit an error in the code below?
我找到了下面的示例 。显然,代码片段中的注释是错误的,因为变量 S::x
被表达式 &S::x
.
ODR 使用
struct S { static const int x = 1; };
void f() { &S::x; } // discarded-value expression does not odr-use S::x
int main(){
f();
}
我知道编译器不需要发出这样的错误,因为 [basic.def.odr]/10 说 "no diagnostic required"。但是为什么链接器没有像下面的代码那样发出关于未定义变量 S::x
的错误?
#include<iostream>
struct S { static const int x = 1; } s;
int main() {
std::cout << &s.x << '\n';
}
你问:
[Why] doesn't the linker emit an error?
一边说:
[basic.def.odr]/10 says "no diagnostic required"
您在 "no diagnostic required" 中回答您自己的问题。不遵守 odr 规则是 Undefined Behavior, the linker could raise an error or build a 4D version of Tetris 并且按照规格仍然可以!
并澄清 &S::x
是否使用 odr x
:
A variable x
whose name appears as a potentially-evaluated expression ex
is odr-used by ex
unless applying the lvalue-to-rvalue conversion to x
yields a constant expression that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion is applied to e, or e is a discarded-value expression.
对象的地址永远不是常量表达式。 S::x
用于 &S::x
。
为了证明最后的断言:
[expr.const]/6
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints [...]
和
[expr.const]/2.7
2) An expression e
is a core constant expression unless the evaluation of e
, following the rules of the abstract machine, would evaluate one of the following expressions:
[...]
2.7) an lvalue-to-rvalue conversion unless it is applied to
(none 以下要点适用:)
2.7.1) a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or
2.7.2)
a non-volatile glvalue that refers to a subobject of a string literal, or
2.7.3)
a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable subobject of such an object, or
2.7.4)
a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e
;
Clearly the comment in the snippet was wrong as the variable S::x
is odr-used by the expression &S::x
.
然后那个表情被放到位地板上。 &S::x;
未使用 ODR。 [basic.def.odr]
第 3 段的变体如下(粗体强调我的):
A variable x
whose name appears as a potentially-evaluated expression ex
is odr-used by ex
unless applying the lvalue-to-rvalue conversion to x
yields a constant expression that does not invoke any non-trivial functions and, if x
is an object, ex
is an element of the set of potential results of an expression e
, where either the lvalue-to-rvalue conversion is applied to e
, or e
is a discarded-value expression.
[expr]
部分第 11 段介绍了丢弃值表达式的概念:
In some contexts, an expression only appears for its side effects. Such an expression is called a discarded-value expression. The expression is evaluated and its value is discarded. The array-to-pointer and function- to-pointer standard conversions are not applied. The lvalue-to-rvalue conversion is applied if and only if the expression is a glvalue of volatile-qualified type and it is one of the following:
- ( expression ), where expression is one of these expressions,
- id-expression,
- subscripting,
- class member access,
- indirection,
- pointer-to-member operation,
- conditional expression where both the second and the third operands are one of these expressions, or
- comma expression where the right operand is one of these expressions.
语句&S::x;
是一个弃值表达式,不需要左值到右值的转换,所以没有什么可以转换的。该声明也可能不存在,因此就 ODR 使用而言不存在。
可以通过将 S::x
限定为 volatile
而不是 const
并尝试将 S::x
降到最低位来更改此指标:
struct S { static volatile int x; };
void f() { S::x; } // This discarded-value expression does odr-use S::x
int main(){
f();
}
上面的代码使用 --std=c++03
使用 GNU 的 C++ compiler/linker 编译(但带有关于丢弃表达式的警告),但使用 --std=c++11
或更高版本无法 compile/link。 C++11 添加了很多关于 C++ 抽象机工作的内容。尽管 S::x;
仍然是一个废弃的值表达式,但 S::x
现在是易变的,需要编译器在将结果放到位层之前应用左值到右值的转换。
将上面的 S::x
更改为 &S::x
,程序再次编译(但再次出现关于丢弃表达式的警告)。尽管 S::x
是可变的,但它的地址不是。
But why doesn't the linker emit an error about the undefined variable S::x, as it does in the code below?
因为简直优化掉了!结果从未使用且没有副作用的表达式将被忽略。并且不能链接被忽略的内容。根本没有引用该变量的代码,即使它的地址已被获取但随后未被使用。
如您的 wandbox 示例所示,编译器发出正确的诊断信息:
"expression result unused"。
未使用的代码以后不会导致链接器错误 ;)
您的第二个示例使用值( var 的地址),因此需要计算表达式。这会向链接器发出代码,其中无法在任何地方找到地址符号。
我找到了下面的示例 S::x
被表达式 &S::x
.
struct S { static const int x = 1; };
void f() { &S::x; } // discarded-value expression does not odr-use S::x
int main(){
f();
}
我知道编译器不需要发出这样的错误,因为 [basic.def.odr]/10 说 "no diagnostic required"。但是为什么链接器没有像下面的代码那样发出关于未定义变量 S::x
的错误?
#include<iostream>
struct S { static const int x = 1; } s;
int main() {
std::cout << &s.x << '\n';
}
你问:
[Why] doesn't the linker emit an error?
一边说:
[basic.def.odr]/10 says "no diagnostic required"
您在 "no diagnostic required" 中回答您自己的问题。不遵守 odr 规则是 Undefined Behavior, the linker could raise an error or build a 4D version of Tetris 并且按照规格仍然可以!
并澄清 &S::x
是否使用 odr x
:
A variable
x
whose name appears as a potentially-evaluated expressionex
is odr-used byex
unless applying the lvalue-to-rvalue conversion tox
yields a constant expression that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion is applied to e, or e is a discarded-value expression.
对象的地址永远不是常量表达式。 S::x
用于 &S::x
。
为了证明最后的断言:
[expr.const]/6
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints [...]
和
[expr.const]/2.7
2) An expression
e
is a core constant expression unless the evaluation ofe
, following the rules of the abstract machine, would evaluate one of the following expressions:
[...]
2.7) an lvalue-to-rvalue conversion unless it is applied to
(none 以下要点适用:)
2.7.1) a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or
2.7.2) a non-volatile glvalue that refers to a subobject of a string literal, or
2.7.3) a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable subobject of such an object, or
2.7.4) a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation ofe
;
Clearly the comment in the snippet was wrong as the variable
S::x
is odr-used by the expression&S::x
.
然后那个表情被放到位地板上。 &S::x;
未使用 ODR。 [basic.def.odr]
第 3 段的变体如下(粗体强调我的):
A variable
x
whose name appears as a potentially-evaluated expressionex
is odr-used byex
unless applying the lvalue-to-rvalue conversion tox
yields a constant expression that does not invoke any non-trivial functions and, ifx
is an object,ex
is an element of the set of potential results of an expressione
, where either the lvalue-to-rvalue conversion is applied toe
, ore
is a discarded-value expression.
[expr]
部分第 11 段介绍了丢弃值表达式的概念:
In some contexts, an expression only appears for its side effects. Such an expression is called a discarded-value expression. The expression is evaluated and its value is discarded. The array-to-pointer and function- to-pointer standard conversions are not applied. The lvalue-to-rvalue conversion is applied if and only if the expression is a glvalue of volatile-qualified type and it is one of the following:
- ( expression ), where expression is one of these expressions,
- id-expression,
- subscripting,
- class member access,
- indirection,
- pointer-to-member operation,
- conditional expression where both the second and the third operands are one of these expressions, or
- comma expression where the right operand is one of these expressions.
语句&S::x;
是一个弃值表达式,不需要左值到右值的转换,所以没有什么可以转换的。该声明也可能不存在,因此就 ODR 使用而言不存在。
可以通过将 S::x
限定为 volatile
而不是 const
并尝试将 S::x
降到最低位来更改此指标:
struct S { static volatile int x; };
void f() { S::x; } // This discarded-value expression does odr-use S::x
int main(){
f();
}
上面的代码使用 --std=c++03
使用 GNU 的 C++ compiler/linker 编译(但带有关于丢弃表达式的警告),但使用 --std=c++11
或更高版本无法 compile/link。 C++11 添加了很多关于 C++ 抽象机工作的内容。尽管 S::x;
仍然是一个废弃的值表达式,但 S::x
现在是易变的,需要编译器在将结果放到位层之前应用左值到右值的转换。
将上面的 S::x
更改为 &S::x
,程序再次编译(但再次出现关于丢弃表达式的警告)。尽管 S::x
是可变的,但它的地址不是。
But why doesn't the linker emit an error about the undefined variable S::x, as it does in the code below?
因为简直优化掉了!结果从未使用且没有副作用的表达式将被忽略。并且不能链接被忽略的内容。根本没有引用该变量的代码,即使它的地址已被获取但随后未被使用。
如您的 wandbox 示例所示,编译器发出正确的诊断信息: "expression result unused"。
未使用的代码以后不会导致链接器错误 ;)
您的第二个示例使用值( var 的地址),因此需要计算表达式。这会向链接器发出代码,其中无法在任何地方找到地址符号。