简体   繁体   English

C++ gcc 7.5 上的隐式转换和通用转发

[英]C++ implicit conversion and universal forwarding on gcc 7.5

I am creating a library component that uses universal forwarding.我正在创建一个使用通用转发的库组件。 This works fine in most cases but I have encountered a case where our linux build (seeminingly) incorrectly uses a copy constructor instead of a move constructor.这在大多数情况下都可以正常工作,但我遇到过这样一种情况,即我们的 linux 构建(看似)错误地使用了复制构造函数而不是移动构造函数。

I was able to reproduce this in godbolt on gcc 7.5 (same as our linux build platform) with a MVE:我能够使用MVE在 gcc 7.5(与我们的 linux 构建平台相同)上的 Godbolt 中重现这一点:

#include <iostream>
using namespace std;

struct A 
{
    A(){}
    A(const A&) = delete;
    A(A&& other){ cout << "move A " << endl; }
};

template<class T>
struct B
{
    template<class U = T>
    B(U&& other)
    : m_a(std::forward<U>(other))
    {
        cout << "forward T into B " << endl;
    }
    T m_a;
};

B<A> foo(A a)
{
    return a;
    //return std::move(a);
}

int main() 
{    
    A a;
    auto b = foo(std::move(a));
}

For clarity I added the non compilable version to illustrate the problem.为了清楚起见,我添加了不可编译的版本来说明问题。

Now, as far as I know I can't use (N)RVO in this case so a move is not inherently wrong but I'd rather avoid writing return move(...);现在,据我所知,在这种情况下我不能使用 (N)RVO,所以移动本身并不是错误的,但我宁愿避免写 return move(...); When compiling this with gcc 8.1 (or a relatively recent MSVC) it does use the move constructor without the move.当使用 gcc 8.1(或相对较新的 MSVC)编译它时,它确实使用了没有移动的移动构造函数。 So is this just a compiler problem or is there something I need to improve on my "B" struct to handle this case?那么这只是一个编译器问题还是我需要改进我的“B”结构来处理这种情况?

The shown program (using return a; ) is well-formed since C++17.所示程序(使用return a; )自 C++17 以来格式良好。 As such, you need a C++17 conforming compiler to compile it.因此,您需要一个符合 C++17 的编译器来编译它。 C++17 support in GCC 7 was experimental (C++17 hadn't been released yet). GCC 7 中的 C++17 支持是实验性的(C++17 尚未发布)。

Using return std::move(a);使用return std::move(a); is well-formed since C++11, and such should work with much older compilers.自 C++11 以来格式良好,并且应该适用于更旧的编译器。


Regarding the different wording that makes it work in C++17:关于使其在 C++17 中工作的不同措辞:

C++14 (draft): C++14(草案):

[class.copy] [class.copy]

When the criteria for elision of a copy/move operation are met , but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of t (3.1) he innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.当满足复制/移动操作的省略条件但不满足异常声明时,并且要复制的 object 由左值指定,或者当返回语句中的表达式是(可能带括号的)id- expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of t (3.1) he innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were由右值指定。

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object...当满足某些条件时,允许实现省略 class object 的复制/移动构造...

  • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object ( other than a function or catch-clause parameter ) in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object ( other than a function or catch-clause parameter )

C++17 (draft): C++17(草案):

[class.copy.elision] [class.copy.elision]

An implicitly movable entity is a variable of automatic storage duration that is either a non-volatile object or an rvalue reference to a non-volatile object type.隐式可移动实体是自动存储持续时间的变量,它可以是非易失性 object 或对非易失性 object 类型的右值引用。 In the following copy-initialization contexts, a move operation is first considered before attempting a copy operation:在以下复制初始化上下文中,在尝试复制操作之前首先考虑移动操作:

  • If the expression in a return ([stmt.return]) or co_return ([stmt.return.coroutine]) statement is a (possibly parenthesized) id-expression that names an implicitly movable entity declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, or如果 return ([stmt.return]) 或 co_return ([stmt.return.coroutine]) 语句中的表达式是一个(可能带括号的)id-expression,它命名在正文或参数声明子句中声明的隐式可移动实体最里面的封闭 function 或 lambda 表达式,或

In short, the automatic move instead of copy from lvalue in return statement rule used to be coupled with the copy/move elision rule which doesn't apply to function parameters.简而言之,return 语句中的自动移动而不是从左值复制规则曾经与不适用于 function 参数的复制/移动省略规则相结合。 Now, they have been decoupled and the former applies explicitly to function parameters as well.现在,它们已经解耦,前者也明确适用于 function 参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM