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

NoS*_*tAl 11 c++ copy-elision rvo stdtuple c++20

I would expect that in C++20 the following code prints nothing between prints of A and B (since I expect guaranteed RVO to kick in). But output is:

A

Bye

B

C

Bye

Bye

So presumably one temporary is being created.

#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";
}
Run Code Online (Sandbox Code Playgroud)

What is the reason for this behavior? Is there a workaround to avoid copy (without using pointers)?

https://godbolt.org/z/zasoGd

son*_*yao 12

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

tuple( const Types&... args );
Run Code Online (Sandbox Code Playgroud)

然后当{i, {47}}用作初始化器时,INeedElision会构造一个临时的然后传递给的构造器std::tuple (and get copied). The temporary object will be destroyed immediately and you'll see "Bye" between "A" and "B".

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

template< class... UTypes >
tuple( UTypes&&... args );
Run Code Online (Sandbox Code Playgroud)

这是一个构造函数模板,和braced-init-list一样 {47}没有类型,不能通过模板参数推导来推导。

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

居住