构造字符串( "Plain Old C chain" ) 是右值吗?

Is constructed string("Plain Old C chain") a rvalue?

本文关键字:chain 字符串 Plain Old      更新时间:2023-10-16

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

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

我在SO上搜索了如何通过编程实现这一点,并找到了这篇文章:如何通过编程确定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< xxx >::value

  • 我哪里错了
  • string("Hello world!")可能是rvalue,但似乎不是"任何类型的引用"(lvaluervalue、通用…),所以我得到了false的结果。如果是rvalue,有没有办法得到true的答案
  • 他的例子中有没有抄袭
  • 我哪里错了

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

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

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

你的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(""))的情况下,您正在创建一个未命名的临时值,它是一个右值。你不需要问它的类型就知道了。

  • 字符串("Hello world!")可能是右值,但似乎不是"任何类型的引用"(左值、右值、通用…),因此我得到了false结果。在右值的情况下,有没有办法得到的答案

你说的"真"答案是什么意思?你只是指一种类型特征,它会给出true的结果吗?

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

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

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

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

  • 在他的例子中是否有省略

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

有关副本省略规则的解释,请访问http://en.cppreference.com/w/cpp/language/copy_elision——看关于一个无名的临时工的部分。

我哪里错了?

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

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

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

字符串("Hello world!")可能是右值,但似乎不是"任何类型的引用">

string("Hello world!")是一个具有右值值类别的表达式,更具体地说,它是一个prvalue。表达式是非引用(更多信息:"在C++中,当decltype应用于哪些表达式时,它们会产生引用类型?"。)


在右值的情况下,有办法得到正确的答案吗?

您可以使用以下转换:

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

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

由于模板参数推导规则,可以获得上述行为:

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&&对于xvaluesprvalues

  • CCD_ 35被推导为CCD_ 36,否则(即对于左值)


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

在C++14中,编译器可以(但不是必需的)来消除示例中的副本。

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

相关C++14标准草案(N4296)报价:

  • §12.8[class.copy]p.31

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

    […]

    当尚未绑定到引用的临时类对象将被复制/移动时,

    [p.3]对于具有相同cv不合格类型的类对象,可以通过将临时对象直接构建到省略的复制/移动的目标中

  • §5.2.2[expr.call]第4页

    当调用一个函数时,每个参数都应初始化为其相应的论点[…]期间参数的初始化,实现可以通过将相关参数的转换和/或临时性的构造与参数的初始化[…]

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