【问题标题】:Conditional operator with same underlying class type具有相同基础类类型的条件运算符
【发布时间】:2017-10-01 19:51:15
【问题描述】:

这个程序应该输出 0 还是 1?在我阅读和理解 C++14 标准中引用的段落时,它应该打印 1,但 GCC 和 clang 都打印 0(因为推断的类型是 A const 而不是 A const&):

#include <iostream>

struct A {};

int main()
{
    A a;
    A const& ra = std::move(a); // #1

    std::cout << std::is_same<decltype(true ? ra : std::move(a)),
                              A const&>::value; // Prints 0
}

在这种情况下,raA const 左值,std::move(a)A xvalue,两者都是类类型。根据关于条件运算符的标准(强调我的),结果应该是A const 类型的lvalue,因此decltype 结果必须是A const&amp;

[expr.cond]/3 否则,如果第二个和第三个操作数具有不同的类型并且两者都有(可能是 cv 限定的)类 type,或者如果两者都是相同值类别和相同类型(除了 cv 限定)的泛左值,则 尝试将这些操作数中的每一个转换为另一个的类型。确定过程 T1 类型的操作数表达式 E1 是否可以转换为匹配类型为 E2 的操作数表达式 T2定义如下:

——如果 E2 是一个左值:如果 E1 可以隐式转换(第 4 条)到 键入“对 T2 的左值引用”,受制于在转换中引用必须直接绑定 (8.5.3) 到左值

[...]

本例中,E2ra,为左值,其他可隐式转换为"lvalue reference to T2",如图线// #1"lvalue reference to T2"翻译为A const&amp;,所以std::move(a)直接绑定到一个A const类型的左值,转换后两个操作数的类型和值类别相同,因此:

[expr.cond]/3 如果第二个和第三个操作数是相同值类别的 glvalues 并且具有相同的类型,则结果是该类型和值类别 [...] .

因此,运算符结果应该是左值,decltype 结果应该是引用,因此程序应该打印 1。

【问题讨论】:

  • 这可能会影响std::common_type 的行为,不是吗?或者至少是默认实现:en.cppreference.com/w/cpp/types/common_type
  • @ChrisBeck 在这种特殊情况下没有,因为模板参数首先被衰减。
  • @Peregring-lk 您应该永远不要修改问题,使其他人的 cmet(或答案)无效。
  • 没关系,他应该只提一下编译器在他修改后的问题中做了什么。评论是轻量级的。无效的答案虽然不好,但应该问一个新问题
  • 这段代码没有类型推导。通过“推导类型”我猜你实际上是指条件运算符的第二个和第三个操作数的常见类型。

标签: c++ c++14 conditional-operator decltype


【解决方案1】:

这个问题措辞很尴尬。相反,您应该询问表达式 true ? ra : std::move(a) 的类型和值类别应该是什么。该问题的答案是A const 类型的纯右值。这随后意味着程序应该打印 0,因为我认为每个编译器都正确。


?: 的规则相当复杂。在这种情况下,我们有两个类类型的表达式,我们尝试查看是否可以根据有限的规则子集相互转换。

尝试转换 rastd::move(a) 失败。我们首先尝试使用target typeA&amp;&amp;,它不能直接绑定到ra。然后我们尝试(3.3.1) 中的备份计划,因为这两个表达式具有相同的底层类类型,但我们的目标表达式至少不像源表达式那样具有 cv 限定,所以这也失败了。

尝试转换 std::move(a)ra 失败 (3.1) 因为我们需要直接绑定到左值(我们可以将右值绑定到 const 左值引用,但这里我们是必需的绑定左值)。但是,(3.3.1) 备份成功了,因为现在目标类型 至少与源类型一样具有 cv 限定。

因此,我们应用转换并继续,就好像第二个操作数是A const 类型的左值,但第三个操作数现在是A const 类型的纯右值(而不是A 类型的xvalue)。

(4) 失败,因为它们的值类别不同。

因此,result is a prvalue。而且由于它们的类型相同,the result is of that type:A const

【讨论】:

  • @Barry “绑定到左值”的规则是什么?关于“引用”绑定的章节谈到了“引用”。 “直接绑定到左值”是什么意思?我了解所有步骤,除了那个改变一切的步骤。
  • @Peregring-lk 我不明白这个问题。措辞要求我们绑定到一个左值,但我们的另一个操作数不是左值——它是一个 xvalue——所以我们不能这样做。
  • @Barry Ahhh 好的,我明白了。要求超过 reference (T const lvalue),而不是必须绑定的操作数 (T xvalue)。我的想法正好相反。
  • @Barry 这里涉及多少“副本”?二不行吗?当通过 (3.3.1) 和 (6.1) 从 xvalue(创建临时值)创建右值以创建最终结果时,即使在我的示例中第一个操作数是 false
  • @Peregring-lk [expr.cond] 中描述的操作实际上不会发生,它们只是确定结果类型。 b ? ra : std::move(a) 是(ra)的副本或移动(a)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 2015-06-09
  • 1970-01-01
  • 2013-09-17
  • 2018-10-24
  • 2015-05-24
  • 1970-01-01
相关资源
最近更新 更多