繁体   English   中英

为什么我不能通过 std::tuple 获得保证的复制省略?

[英]Why do I not get guaranteed copy elision with std::tuple?

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

一种

再见

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

当从{i, {47}}构造std::tuple<int, INeedElision> std::tuple的选定构造函数通过对const左值引用获取元素。

 tuple( const Types&... args );

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

顺便说一句: std::tuple的第三个构造函数不会用于这种情况。

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

它是一个构造函数模板,像{47}这样的花括号初始化列表没有类型并且不能通过模板参数推导来推导。

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

居住

如果您返回对象本身,您只会得到复制省略:

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;
}

暂无
暂无

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

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