【问题标题】:Is constructed string("Plain Old C chain") a rvalue?构造的 string("Plain Old C chain") 是右值吗?
【发布时间】:2017-02-06 11:35:08
【问题描述】:

我想知道是否在下面的foo(string) 调用中进行了复制省略。 (注:foo(string)属于我无法更改的接口)。

为此,我尝试检查构造的string("Hello world!") 是否为rvalue

我搜索了如何以编程方式执行此操作并找到了这篇文章:How to determine programmatically if an expression is rvalue or lvalue in C++?

void foo( string str)
{
    cout << str << endl;
}

int main()
{
    foo("Hello world!");
    cout << is_rvalue_reference<decltype(string("Hello world!"))>::value  << endl;
}

结果是

Hello world!
0

我以为我会得到trueis_rvalue_reference&lt; xxx &gt;::value

  • 我哪里错了?
  • string("Hello world!") 可能是 rvalue 但似乎不是“任何类型的参考”(lvaluervalue、通用 ...)所以我得到了 false 结果。如果是rvalue,有没有办法获得true 的答案?
  • 他的示例中是否存在复制省略?

【问题讨论】:

    标签: c++


    【解决方案1】:
    • 我哪里错了?

    std::string("") 是一个右值,但decltype(std::string("")) 不是一个右值引用。 std::string 对象的类型当然是...std::string

    您有一个类别错误。右值是一种表达式,右值引用是一种类型。

    一个临时的string 对象是一个右值。 string&amp;&amp; 类型是右值引用类型。

    您的decltype 表达式对于您要执行的操作没有用处。考虑:

    std::string s;
    using type1 = decltype(s);
    using type2 = decltype(std::string(""));
    static_assert(std::is_same<type1, type2>::value, "same");
    

    在这两种情况下,decltype 给出相同的类型:std::string。那是因为decltype 告诉您的是类型,而不是值类别(即表达式是右值还是左值)。

    如果你想知道一个表达式是右值还是左值,你需要知道的不仅仅是它的类型。在decltype(std::string("")) 的情况下,您正在创建一个未命名的临时对象,它是一个右值。你不需要问它的类型就知道了。

    • string("Hello world!") 可能是右值,但似乎不是“任何类型的引用”(左值、右值、通用 ...),所以我得到了 错误 结果。如果是右值,有没有办法获得 true 答案?

    你所说的“真实”答案是什么意思?你的意思是一个类型特征会给出结果true吗?

    您可以询问该类型是否可转换为右值引用:

    std::is_convertible<decltype(std::string("")), std::string&&>::value
    

    这将告诉您是否可以将右值引用绑定到对象。但这是一个愚蠢的问题:当然,您可以将 X&& 类型的右值引用绑定到 X 类型的临时对象。您永远不需要问这个问题。

    无论如何,你的函数不接受string&amp;&amp; 类型的参数,所以问这个问题甚至不会告诉你任何关于你对foo(std::string) 的调用。

    • 在他的示例中是否存在复制省略?

    是的,从临时字符串初始化函数参数不应该进行任何复制或移动,它们应该被省略。 C++14 标准在 [class.copy] p31 中说,当临时对象(尚未绑定到引用)将被复制/移动到相同类型的类对象时,可以省略复制/移动。从相同类型的临时变量初始化类类型的函数参数时满足该条件。不执行该省略的编译器(至少在启用优化时,或在“发布”版本中)是一个糟糕的编译器。

    http://en.cppreference.com/w/cpp/language/copy_elision 有对复制省略规则的解释——参见关于无名临时的部分。

    【讨论】:

    • 您能否为 复制省略 段落添加标准引号?我在 [class.copy.elision] 中找不到合适的。
    • @LightnessRacesinOrbit,no。该标准说明何时允许省略(并且在其他地方不允许,否则将是不合格的实现)。编译器应该尽可能地省略。并非所有编译器都这样做。 GCC、Clang 和 Intel 都省略了链接示例。
    • 这就是为什么我说“不应该”。不是“不会”。我的意思正是我写的。
    • 然后写下你自己的答案。问题是“是否存在复制省略”,我的 coliru 演示在那里显示。我说那里应该省略,这与公认的规范写作一致,意味着可以预期但不是必需的。 “应该”这个词暗示标准规定的某些内容,它在 ISO(和其他)标准中具有特定含义。
    • @M.M 我认为在 ISO 标准中只有“应该”意味着严格的要求,而不是“应该”。
    【解决方案2】:

    我哪里错了?

    右值引用具有以下形式:T&amp;&amp;

    decltype(string("")) 计算结果为 string,而不是 string&amp;&amp; - 因此它不是 右值引用

    is_same<decltype(string("Hello world!")), string>::value // true
    

    string("Hello world!") 可能是右值,但似乎不是“任何类型的引用”

    string("Hello world!") 是一个具有 rvalue 值类别的表达式 - 更具体地说,它是一个 prvalue。表达式are non-references(更多信息:"In C++, what expressions yield a reference type when decltype is applied to them?"。)


    如果是右值,有没有办法得到正确的答案?

    您可以使用以下转换:

    • 给定一个左值,返回一个左值引用

    • 给定一个右值,返回一个右值引用

    上面提到的行为可以通过template argument deduction rules获得:

    template <typename T>
    using is_rvalue = std::is_rvalue_reference<T&&>;
    
    is_rvalue<decltype(string(""))>::value // true
    is_rvalue<decltype(std::declval<string&&>())>::value // true
    is_rvalue<decltype(std::declval<string&>())>::value // false
    

    上面的代码之所以有效,是因为:

    • T&amp;&amp; 对于 xvaluesprvalues 都被推导出为 T&amp;&amp;

    • T&amp;&amp; 被推导出为T&amp; 否则(即对于左值)


    他的例子中是否存在复制省略?

    在 C++14 中,编译器被允许 (但不是必需的) 删除示例中的副本。

    (感谢 Jonathan Wakely 识别下面引用的相关 C++14 标准草案引用。)

    相关C++14标准草案(N4296)引用:

    • §12.8 [class.copy] p.31

      当满足某些条件时,允许实现省略类的复制/移动构造 对象,即使为复制/移动操作选择了构造函数和/或对象的析构函数 有副作用。在这种情况下,实现会处理省略的复制/移动的源和目标 操作只是引用同一对象的两种不同方式,以及该对象的销毁 发生在两个对象在没有优化的情况下被销毁的较晚时间。这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的(其中 可以合并以消除多个副本):

      [...]

      [p.31.3] 未绑定到引用的临时类对象将被复制/移动 对于具有相同 cv-unqualified 类型的类对象,可以通过以下方式省略复制/移动操作 将临时对象直接构造到省略的复制/移动的目标中

    • §5.2.2 [expr.call] p.4

      当一个函数被调用时,每个参数都应该用它对应的初始化 争论。 [...] 在 参数的初始化,实现可以通过以下方式避免构建额外的临时变量 将相关参数的转换和/或临时结构的构造与 参数的初始化。 [...]

    ISO/IEC。 (2014)。 ISO 国际标准 ISO/IEC 14882:2014(E) – 编程语言 C++。 [工作草案]。瑞士日内瓦:国际标准化组织 (ISO)。 (取自https://isocpp.org/std/the-standard

    【讨论】:

    • “这已经在标准中并称为 std::forward” 但是你的示例不使用 std::forward
    • 我认为只说“它不是参考”有点误导。表达式具有类型(绝不是引用类型)和值类别。
    • @LightnessRacesinOrbit:哎呀,当我改变我的方法时,我忘了改变它(我之前使用的是std::forward。 @MM:希望现在更准确。
    猜你喜欢
    • 2016-12-23
    • 1970-01-01
    • 2012-06-05
    • 2012-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多