【问题标题】:What are unevaluated contexts in C++?什么是 C++ 中未评估的上下文?
【发布时间】:2016-01-29 15:52:47
【问题描述】:

经常想到的一个例子是:

sizeof 表达式,它不计算表达式,而是通过静态类型确定大小。例如:

int func();
sizeof(func());

这是我的思维局限,所以如果还有其他未评估的上下文,那么它们是什么?

【问题讨论】:

  • 我真的很惊讶以前没有问过这个问题(或者我只是没有找到它)。这是一个很好的。除了sizeof,我只知道decltypenoexcept
  • auto 用于声明和定义变量的关键字?
  • @HappyCoder 我觉得你搞混了。
  • 请解释一下? @bolov
  • 如果我写auto x = foo(); 并且foo() 没有被评估,我会非常沮丧。

标签: c++ c++11


【解决方案1】:

幸运的是,该标准有一个方便的列表(§ 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 对象。左值到右值 (4.1)、数组到指针 (4.2) 和函数到指针 (4.3) 转换不适用于表达式。如果表达式的类型是类类型,则类应该是完全定义的。 表达式是未计算的操作数 (第 5 条)。

注意多态类(class 至少有一个 virtual 成员)的突出例外。

所以没关系

typeid( foo() )

并为int 生成一个std::type_info 对象,而这

typeid( widget::get_instance() )

不是并且可能会产生链接时错误。它必须评估操作数,因为动态类型是通过在运行时查找 vptr 来确定的。

操作数的静态类型是否为多态这一事实以如此戏剧性但微妙的方式改变了运算符的语义,这让我感到非常困惑。

sizeof

§ 5.3.3 [expr.sizeof] ¶ 1:

sizeof 运算符在其操作数的对象表示中产生字节数。 操作数要么是一个表达式,它是一个未计算的操作数(第 5 条),要么是一个带括号的 type-idsizeof 运算符不得应用于具有函数或不完整类型的表达式、其基础类型在其所有枚举数被声明之前未固定的枚举类型、此类类型的括号名称或泛左值指定一个位域。

以下

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;

声明int 类型的变量n 并将其初始化为值42。

auto baz() -> decltype( widget::get_instance() );

声明一个不带参数的函数baz,而returns 是widget&

仅此而已(从 C++14 开始)。

【讨论】:

  • 在 cmets 中有一点,auto 也参与其中。另一个答案也提到了auto,但不清楚。是这样吗?
  • auto 在与decltype 相同的部分中介绍,但我不明白它会是一个未评估的上下文。它甚至不需要操作数。 § 7.1.6.2 中唯一出现的“未评估”一词是在我的回答中引用的段落中。但是 IANAL。
  • @5gon12eder 为什么typeid(*singleton::get_instance()) 不起作用?
  • @vsoftco 因为singleton 是多态的,所以表达式会生成运行时代码以跟随vptr。这意味着对未定义函数singleton::get_instance 的实际调用。请参阅标准引文中第一个突出显示的句子。就个人而言,我觉得这种棘手的差异有点尴尬。
  • @Deduplicator 我也在考虑这个问题。但即使在过去,他们也可以提供一个static_typeid,它从不评估其操作数并为静态类型返回一个std::type_info,以及一个始终评估其操作数并为动态类型返回std::type_infostd::type_info。对于非多态类型将始终相同。
【解决方案2】:

标准术语是一个未计算的操作数,你可以在[expr]中找到它

在某些情况下,会出现未计算的操作数(5.2.8、5.3.3、5.3.7、7.1.6.2)。未计算的操作数不会被计算。未计算的操作数被视为完整表达式。 [注意:在未计算的操作数中,可以命名非静态类成员(5.1),并且对象或函数的命名本身并不要求提供定义(3.2)。 ——尾注]

  • 5.2.8 覆盖typeid
  • 5.3.3 覆盖sizeof
  • 5.3.7 覆盖noexcept
  • 7.1.6.2 涵盖了简单的类型说明符,例如 autodecltype 以及 POD 类型,例如 intchardouble 等。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-20
    • 2023-03-18
    • 2012-03-12
    • 2020-02-04
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多