经常浮现在脑海中的一个例子是:
sizeof
表达式,它不计算表达式,但按静态类型确定大小。例如:
int func();
sizeof(func());
这是我思考的极限,所以如果还有其他未评估的背景,那么它们是什么?
答案 0 :(得分:38)
幸运的是,该标准有一个方便的清单(§5[expr]¶8):
在某些情况下,未评估的操作数出现(5.2.8,5.3.3,5.3.7,7.1.6.2)。未评估未评估的操作数。未评估的操作数被视为完整表达式。
让我们详细看看这些。
我将在我的示例中使用以下声明。声明的函数永远不会在任何地方定义,因此如果对它们的调用出现在评估的上下文中,则程序格式错误,我们将收到链接时错误。然而,在未评估的背景下调用它们是可以的。
int foo(); // never defined anywhere
struct widget
{
virtual ~widget();
static widget& get_instance(); // never defined anywhere
};
typeid
§5.2.8[expr.typeid]¶3:
当
typeid
应用于多态类类型的glvalue以外的表达式时,结果引用表示表达式的静态类型的std::type_info
对象。 Lvalue-to-rvalue(4.1),数组到指针(4.2)和函数到指针(4.3)转换不应用于表达式。如果表达式的类型是类类型,则应完全定义类。 表达式是未评估的操作数 (第5条)。
请注意多态类的强调异常(class
至少有一个virtual
成员)。
因此,这没关系
typeid( foo() )
并为std::type_info
生成int
对象,而此
typeid( widget::get_instance() )
不会,并且可能会产生链接时错误。它必须评估操作数,因为动态类型是通过在运行时查找vptr
来确定的。
< rant>我发现操作数的静态类型是否是多态的这一事实以如此戏剧性但微妙的方式改变了运算符的语义,这让我感到非常困惑。< / rant>
sizeof
§5.3.3[expr.sizeof]¶1:
sizeof
运算符产生其操作数的对象表示中的字节数。 操作数是表达式,它是未评估的操作数(第5条),或带括号的 type-id 。sizeof
运算符不应该应用于具有函数或不完整类型的表达式,也不应用于在声明所有枚举数之前其基础类型未修复的枚举类型,此类型的带括号的名称,或者应用于glvalue指定一个位域。
以下
sizeof( foo() )
非常精细,相当于sizeof(int)
。
sizeof( widget::get_instance() )
也是允许的。但请注意,它等同于sizeof(widget)
,因此对于多态return
类型可能不太有用。
noexcept
§5.3.7[expr.unary.noexcept]¶1:
noexcept
运算符确定对其操作数的评估,即未评估的操作数(第5条)是否可以抛出异常(15.1)。
表达式
noexcept( foo() )
有效且评估为false
。
这是一个更实际的例子,也是有效的。
void bar() noexcept(noexcept( widget::get_instance() ));
请注意,只有内部noexcept
是运算符,而外部是说明符。
decltype
§7.1.6.2[dcl.type.simple]¶4.4:
decltype
说明符的操作数是未评估的操作数(第5条)。
声明
decltype( foo() ) n = 42;
声明n
类型的变量int
,并使用值42初始化它。
auto baz() -> decltype( widget::get_instance() );
声明一个不带参数的函数baz
,return
一个widget&
。
这就是全部(从C ++ 14开始)。
答案 1 :(得分:7)
标准术语是未评估的操作数,您可以在[expr]中找到它
在某些情况下,出现未评估的操作数(5.2.8,5.3.3,5.3.7,7.1.6.2)。未评估未评估的操作数。未评估的操作数被视为完整表达式。 [注意:在未评估的操作数中,可以将非静态类成员命名为(5.1),并且对象或函数的命名本身不要求提供定义(3.2)。 - 后注]
typeid
sizeof
noexcept
auto
和decltype
以及POD类型,例如int
,char
,double
等。