简体   繁体   中英

Returning a tuple from a function using uniform initialization syntax

The following code compiles with clang (libc++) and fails with gcc (libstdc++). Why does gcc (libstdc++) complains about an initializer list? I thought the return argument was using uniform initialization syntax.

std::tuple<double,double> dummy() {
  return {2.0, 3.0};
}

int main() {   
  std::tuple<double,double> a = dummy();   
  return 0;
}

Error: line 22: converting to 'std::tuple' from initializer \\ list would use explicit constructor 'constexpr std::tuple<_T1, _T2>::tuple(_U1&\\ &, _U2&&) [with _U1 = double; _U2 = double; = void; _T\\ 1 = double; _T2 = double]'

Note: GCC (libstdc++) (and clang (libc++)) accept

std::tuple<double,double> dummy {1.0, 2.0};

Isn't it the same case?

Update: this is a libc++ extension, see http://llvm.org/bugs/show_bug.cgi?id=15299 and also answer by Howard Hinnant below.

Unlike for pair<> , implicit construction of a tuple<> is not possible unfortunately. You have to use make_tuple() :

#include <tuple>

std::tuple<double,double> dummy()
{
    return std::make_tuple(2.0, 3.0); // OK
}

int main() 
{   
    std::tuple<double,double> a = dummy();   
    return 0;
}

std::tuple has a variadic constructor, but it is marked as explicit . Thus, it cannot be used in this situation, where a temporary must be implicitly constructible. Per Paragraph 20.4.2 of the C++11 Standard:

namespace std {
    template <class... Types>
    class tuple {
    public:

        [...]
        explicit tuple(const Types&...); // Marked as explicit!

        template <class... UTypes>
        explicit tuple(UTypes&&...);     // Marked as explicit!

For the same reason it is illegal to use copy-initialization syntax for initializing tuples:

std::tuple<double, double> a = {1.0, 2.0}; // ERROR!
std::tuple<double, double> a{1.0, 2.0}; // OK

Or to construct a tuple implicitly when passing it as an argument to a function:

void f(std::tuple<double, double> t) { ... }
...
f({1.0, 2.0}); // ERROR!
f(make_tuple(1.0, 2.0)); // OK

Accordingly, if you construct your std::tuple explicitly when returning it in dummy() , no compilation error will occur:

#include <tuple>

std::tuple<double,double> dummy()
{
    return std::tuple<double, double>{2.0, 3.0}; // OK
}

int main() 
{   
    std::tuple<double,double> a = dummy();   
    return 0;
}

The answer Andy Prowl gives is correct. I wanted to comment on the libc++ implementation, and the comment format does not allow me enough room or formatting choices.

Daniel Krügler and I had a conversation about a year ago on this subject, and he convinced me that this issue was worthy putting an extension into libc++ to get field experience with. And so far the feedback has been positive. However I want to make clear: This is not as simple as removing explicit from the ctor explicit constexpr tuple(UTypes&&...) .

Daniel's plan is to give tuple a constructor that perfectly respects each element's implicit/explicit construction. And if every element will construct implicitly from every argument in the initializer list, then the tuple construction is implicit, else it will be explicit.

For example:

Given:

#include <tuple>

struct A
{
};

struct B
{
    B() = default;
    B(A);
};

struct C
{
    C() = default;
    explicit C(A);
};

Then:

std::tuple<>
test0()
{
    return {};  // ok
}

Nothing much to say about that one. But also this is ok:

std::tuple<B>
test1B()
{
    return {A()};  // ok B(A) implicit
}

because the conversion from A to B is implicit. However the following is a compile time error:

std::tuple<C>
test1C()
{
    return {A()};  // error, C(A) is explicit
}

because the conversion from A to C is explicit. This logic continues for multi-element tuples. For an implicit conversion to happen, every element must have an implicit conversion from the argument list:

std::tuple<A, B>
test2B()
{
    return {A(), A()};  // ok each element has implicit ctor
}

std::tuple<A, C>
test2C()
{
    return {A(), A()};  // error, C(A) is explicit
}

I should emphasize: This is a libc++ extension at this time.

Update

chico made a good suggestion that I update this answer:

Since this answer was given, Daniel Krügler wrote a paper and presented it to the C++ committee in Bristol this past April. Although the paper was well received, it was reviewed too late in the week to vote it into the current working draft.

Update

Daniel's proposal is now part of the current working draft . The libc++ implementation is now set to become standard in this regard for the next C++ standard (C++17 we hope).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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