左值和右值混合使用的三元运算符的类型推导

问题描述

最近,我偶然发现自己的代码是这样写的(这里简化了很多):

#include <iostream>
#include <string>

void foo(std::string&& value)
{
  std::cout << value << std::endl;
  // for instance modify value here somehow optionally and move it further on ...
  // foo2(std::move(value));
}

int main()
{
  std::string value = "Hello world";

  for (size_t idx = 0; idx < 2; ++idx)
  {
    foo(idx < 1 ? value : std::move(value));
  }
}

请不要对有问题的计划发表评论!这发生在一个漫长的夜晚会议... :)

我只想知道,就标准行为而言,这里实际发生了什么。三元运算符的类型推导显然会导致右值,但我想知道标准中的确切拟合短语,因为可观察到的效果是对于 idx

我读了 https://timsong-cpp.github.io/cppwp/n4659/expr.cond 但这似乎不足以解释这里的行为,因为我期望直接右值转换(虽然最初我期望编译器错误)。我还认为这里可能有一些关于此的重复,但到目前为止我无法在这里找到真正合适的。但是,如果有相当快的可检测到的,请提前抱歉!

解决方法

我是这样看的:

idx < 1 ? value : std::move(value) 

是一个表达式。

计算这个表达式的结果是一个右值总是

idx < 1 的情况下:由该表达式计算的 rvaluevalue 中的 copy-constructing 构造。

idx >= 的情况下:由该表达式计算的 rvaluemove-constructing 中的 value 构造。

所以最后,void foo(std::string&& value) 中的函数参数值总是通过计算这个表达式绑定到一个右值。

只是这个表达式值本身的构造方式不同:第一种情况是通过复制构造,第二种情况是通过移动构造。

,

根据 cppinsights.io,编译器正在创建临时 std::string

foo((idx < 1 ? std::basic_string<char,std::char_traits<char>,std::allocator<char> >(value) : std::basic_string<char,std::allocator<char> >(std::move(value))));

这里是将三元运算符与函数调用分开的版本:

auto&& x = idx < 1 ? value : std::move(value);
foo(std::move(x));

cppinsights.io

std::basic_string<char,std::allocator<char> > && x = (idx < 1 ? std::basic_string<char,std::allocator<char> >(std::move(value)));
foo(std::move(x));

我试图理解阅读标准 n4835.pdf,我希望这是正确的:

7.6.16 条件运算符 [expr.cond]
4 否则,如果第二个和第三个操作数具有不同的类型并且具有(可能有 cv 限定的)类类型,或者如果两者都是相同值类别和相同类型的泛左值,除了 cv 限定,尝试形成从每个操作数到另一个操作数的类型的隐式转换序列 (12.4.3.1)。 [...] 尝试形成从 T1 类型的操作数表达式 E1 到与操作数表达式 E2 的类型 T2 相关的目标类型的隐式转换序列,如下所示:
(4.1) — 如果 E2 是左值,则目标类型是“对 T2 的左值引用”,受制于在转换中引用必须直接绑定 (9.4.3) 到泛左值的约束。
(4.2) — 如果 E2 是 xvalue,则目标类型是“对 T2 的右值引用”,受引用必须直接绑定的约束。
(4.3) — 如果 E2 是纯右值,或者如果上述转换序列都不能形成并且至少一个操作数具有(可能是 cv 限定的)类类型:
[...] (4.3.3) — 否则,目标类型是 E2 在应用左值到右值后的类型 (7.3.1),

4.1 和 4.2 不适用,所以我们来到 4.3.3。到 7.3.1,其中 3.2 适用。

7.3.1 左值到右值的转换 [conv.lval] 3 转换结果根据以下规则确定:
[...] (3.2) — 否则,如果 T 具有类类型,则转换会从泛左值复制初始化结果对象。

请注意,7.6.16 4 说转换是双向尝试的,因此不要过多关注 4.3(.3) 中的“E2”。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...