为什么不能使用std :: tuple获得保证的复制省略?

问题描述

我希望在C ++ 20中,以下代码在A和B的打印之间不会打印任何内容(因为我希望可以保证RVO起作用)。但是输出是:

A

再见

B

C

再见

再见

因此大概正在创建一个临时文件。

#include <iostream>
#include <tuple>
struct INeedElision{
    int i;
    ~INeedElision(){
        std::cout << "Bye\n";
    }
};

std::tuple<int,INeedElision> f(){
    int i = 47;
    return {i,{47}};
}

INeedElision g(){
    return {};
}

int main()
{   
    std::cout << "A\n"; 
    auto x = f();
    std::cout << "B\n";
    auto y = g();
    std::cout << "C\n";
}

此行为的原因是什么? 有没有避免复制的解决方法(不使用指针)?

https://godbolt.org/z/zasoGd

解决方法

std::tuple<int,INeedElision>构造{i,{47}}时,the selected constructor of std::tuple通过对const的左值引用来获取元素。

tuple( const Types&... args );

然后,在使用{i,{47}}作为初始化程序时,将构造一个临时INeedElision,然后将其传递给std::tuple的构造函数(并进行复制)。临时对象将被立即销毁,并且您会在“ A”和“ B”之间看到“再见”。

顺便说一句:std::tuple的第3个构造函数在这种情况下将不会使用。

template< class... UTypes >
tuple( UTypes&&... args );

这是一个构造函数模板,并且{47}之类的括号初始列表没有类型,也无法通过模板参数推导来推导。

另一方面,如果INeedElision的转换构造函数使用int,并将初始化程序设为{i,47},则将使用std::tuple的第三个构造函数,并且不使用构造临时INeedElision;元素将从int 47就地构建。

LIVE

,

仅当您返回对象本身时,才会得到复制省略:

std::vector<int> fn1()
{
   return std::vector<int>{}; // guaranteed copy elision
}

std::vector<int> fn2()
{
   std::vector<int> vec;
   return vec; // a good compiler will manage to elide the copy/move here
}

在您的情况下,您将返回元组,因此元组本身可能会被复制,但不会传递给元组的构造函数的参数!

std::tuple<int,INeedElision> f(){

    int i = 47;
    return {i,{47}}; // construct the tuple in place of the return address but the arguments are copied into the tuple and not even moved ! to move call std::move explicitly
}

不允许编译器取消传递给元组构造函数的参数副本,因为您不是返回参数本身,而是返回包含参数副本的元组。还要注意,表不能保存对参数的引用,因为这些局部变量在函数返回时将被破坏,从而导致悬挂的引用。

如果您想在c ++ 17中获得复制省略的机会,然后再执行以下操作:

std::tuple<int,INeedElision> f(){

    std::tuple<int,INeedElision> ret;
    auto& [i,ne] = ret;
    i = 47;
    ne = 47;
    return ret;
}

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...