【问题标题】:What is calling void(); doing? [duplicate]什么在调用 void();正在做? [复制]
【发布时间】:2020-04-11 21:02:35
【问题描述】:

我发现void(); 在三元运算符的“else”分支中用作“无所事事”,作为空指针检查的简写

if(var){
   var->member();
}

作为

var ? var->member() : void();

但我似乎找不到以这种方式使用的void 关键字的任何引用,这是对void 关键字本身的函数或仿函数调用吗?或者它没有对void 类型进行任何转换?还是这只是 pass 之类的 c++ 语法?

编辑: 在这种情况下member() 的返回类型是void

【问题讨论】:

  • 如果你完全忘记你看过这段代码,你会帮自己一个忙,并保证你自己永远不会写出这么丑陋的东西。
  • 你应该问的真正问题是,当前者像你永远需要的那样富有表现力且意图清晰时,这个替代方案是愚蠢的,而后者似乎有意向代码读者否认这两个属性。
  • @SamVarshavchik:某些语言(如 C#)具有空条件或空合并(或 Elvis)运算符。对于来自这种语言的人来说,在 c++ 中使用同一语言可能是有意义的。
  • 为什么有人会写这个而不是if (var) var->member(); one-liner?
  • @JosephSible-ReinstateMonica 原来我一定是记错了。作为@T.C.在我的(现已删除的)答案中正确指出,void 不允许作为 C++11 中constexpr 函数的类型,这使我的答案毫无意义。

标签: c++ syntax void


【解决方案1】:

您只是在“构造”void 类型的纯右值(不是变量,因为 cmets 中建议的原因),就像 int() 会默认构造一个 int

正如其他人在 cmets 中所说,第二种选择是贬义的。三元运算符是三元,因为它具有ifthenelse 部分。如果您不需要else,为什么要写一个并将其留空?

那个替代方案比这个更丑陋、更神秘:

if(var){
   var->member();
} else {}

这可能看起来很愚蠢。

【讨论】:

  • @WhozCraig 奇怪的是,[expr.type.conv] 有这样的说法:“如果类型是 cv void 并且初始化程序是 (),则表达式是不执行初始化的指定类型的纯右值。”这表明有可能有一个类型为void 的纯右值(虽然不是左值,这就是“变量”的含义)。
【解决方案2】:

假设var->member() 的类型为void

var ? var->member() : void();

具有void 类型,如果var 为空,则评估var->member() 或评估void()

现在,void() 是一个表达式;根据[expr.type.conv]/2,它什么也不做:

如果类型是 cv void 并且初始值设定项是 (){}(在包扩展之后,如果有的话),则表达式是 prvalue不执行初始化的指定类型。

【讨论】:

  • 而且(唉)void() 不是一个值,也不是一个虚拟的非值。可以用作void Foo() { return void(); }(为什么要这样做?T 为void 的模板,您需要返回T(),而您不必为了处理T = void 边缘而进行SFINAE 恶作剧案子)。但不能用于调用Foo(void());,因为这太明显了。 C++ 没有实际的底部类型。它几乎没有单元类型(即std::monostate ...但它也有0元组std::tuple<>和任何退化结构struct Unit_t {};)。或者“退化”结构的正确术语。
  • void foo() { } 应该能够与void bar() { return foo(); }auto baz() { return foo(); }void quux(bool pred) { return pred ? foo() : void(); } 一起使用。
【解决方案3】:

void 是一个类型,但如果它后面跟着(),它会初始化prvalue 类型的void

这样做的原因var->member()的返回类型是void,而?:运算符的第二个和第三个操作数必须相同。注意:它们不必完全相同;有conversion rules 说什么时候类型可以不同。

您已经展示了一种为其中一个操作数获取void prvalue 的方法,但是有多种方法可以达到相同的效果,

var ? var->member() : throw 42;

throw 表达式具有 void 类型,因此可以编译。如果varnullptr 当然,它不会做nothing,因为它会抛出。

这条语句将编译并且什么也不做,

var ? var->member() : []{}();

第二个操作数是返回void的匿名函数。

还有这个,

var ? var->member() : decltype(var->member())(); 

在我看来,这句话说得最清楚,“我试图在两个操作数中获得相同的类型”。

话虽如此,我不明白为什么有人会编写这段代码。如果没有有意义的 else 分支,那么语言中已经存在 if 构造,而条件 ?: 运算符是不适合这项工作的工具。

编辑:@walnut(现已删除)的答案实际上显示了一个用例:在 c++11 中,但在 c++14 之前,?: 运算符是在 constexpr 函数中表达条件分支的唯一方法。

【讨论】:

  • ?: 运算符的第二个和第三个操作数必须相同” - 不是真的。 throw 表达式可以与任何其他表达式配对,cv void 类型必须与另一个 cv void 类型配对,否则当类型不同时必须可以从一种类型隐式转换为另一种类型。
  • @aschepler 谢谢,我添加了一个警告和规则链接。
【解决方案4】:

我只见过“老司机”写过这样的代码,或者可以这么说。 void() 调用基本上什么都不做,并且三元运算符要求您将 something 放在 else 分支中,因此人们有时会这样做。 if (stuff) { stuff->member(); } 也可以是单线。

【讨论】:

  • 你甚至不需要{},如果它只是一个声明。
  • 这没有回答问题。 OP 知道这在这里用作无操作,并希望从语法上理解它。
  • 我认为 OP 的问题是:“或者这只是类似于 pass 的 c++ 语法?”我的回答指出(或无论如何要说明)“三元运算符要求您将 something 放在 else 分支中”。也许我应该说得更清楚。
  • stuff ? stuff->member() : throw my_null_error("stuff"); 也可以工作,作为三元的特例,两个分支的类型不同。
猜你喜欢
  • 2013-04-08
  • 2014-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-15
  • 2014-02-20
相关资源
最近更新 更多