Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I need to specify the type of a default constructed object in this situation?

I don't understand why in foobar below I need to specify std::vector<int>{} whereas in foobar2 I do not:

#include <iostream>
#include <memory>
#include <vector>
#include <tuple>

std::tuple<std::unique_ptr<int>, std::vector<int>> foobar() {
    std::unique_ptr<int> test = std::make_unique<int>(42);
    return { std::move(test), {} };    // <= this is a syntax error
    // return { std::move(test), std::vector<int>{} }  // <= this compiles...
}

std::tuple<int, std::vector<int>> foobar2() {
    return { {},  {} };
}

int main() {
    std::cout << *std::get<0>(foobar()) << "\n";
    std::cout << std::get<0>(foobar2()) << "\n";
    return 0;
}

The error message from GCC is

<source>: In function 'std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::vector<int, std::allocator<int> > > foobar()':
<source>:8:34: error: could not convert '{std::move<unique_ptr<int>&>(test), <brace-enclosed initializer list>()}' from '<brace-enclosed initializer list>' to 'std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::vector<int, std::allocator<int> > >'
    8 |     return { std::move(test), {} };    // <= this is a syntax error
      |                                  ^
      |                                  |
      |                                  <brace-enclosed initializer list>
Compiler returned: 1
like image 397
jwezorek Avatar asked Dec 19 '25 02:12

jwezorek


1 Answers

Given a

template< class... Types >
class tuple;

There are two possible constructors that can be used here. The first one is:

tuple( const Types&... args );

However it can only be used if all tuple members are copy-constructible. The unique_ptr is, of course, not copy-constructible.

This leaves only one other possible constructor:

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

That is, a forwarding constructor, a "Hail Mary" that forwards all its parameters to the constructor of each underlying tuple member.

{}

An empty braced-init list is typeless, and cannot be bound to a forwarding reference.

This could possibly work if only there was one more constructor:

tuple(Types && ... Args);

that participates in overload resolution if all member types are movable. Alas, there isn't.

whereas in foobar2 I do not:

foobar2's tuple members are copy-constructible. The first constructor overload gets used.

like image 180
Sam Varshavchik Avatar answered Dec 20 '25 19:12

Sam Varshavchik



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!