23

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.

gnzlbg
  • 7,135
  • 5
  • 53
  • 106
  • Just commenting on your last question: No, it's not the same case. A return statement is a "copy initialization" context whereas your last example is actually a "direct initialization". The difference between both is that for copy initialization only non-explicit constructors are considered. – sellibitze Feb 20 '13 at 14:11
  • Also Visual Studio 14 Update 3 accepts it. – Liviu Aug 22 '16 at 09:02

2 Answers2

34

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;
}
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • 2
    So clang is wrong to accept this? I thought the return type could be initialized using uniform initialization (not an initializer list). – gnzlbg Feb 19 '13 at 16:03
  • @gnzlbg: It is not a matter of compiler, rather of Standard Library implementation. Clang 3.2 does not compile this [here](http://liveworkspace.org/code/4hDrsS$7). – Andy Prowl Feb 19 '13 at 16:06
  • Ok, I'll rephrase the question then. Is then libc++ wrong to accept this? – gnzlbg Feb 19 '13 at 16:07
  • @gnzlbg: Yes, this should not compile. – Andy Prowl Feb 19 '13 at 16:07
  • 6
    We should write a petition to the committee to make it non explicit for all arities > 1... – PlasmaHH Feb 19 '13 at 16:08
  • @PlasmaHH: Indeed, I really don't see why this is marked as `explicit`. – Andy Prowl Feb 19 '13 at 16:12
  • @PlasmaHH, that would be http://cplusplus.github.com/LWG/lwg-active.html#2051 (which I do not support FWIW, I'd rather have language support for calling explicit constructors in a return statement, e.g. `return auto{1,2,3}` or something else that is applicable to more than just `tuple`) – Jonathan Wakely Feb 19 '13 at 16:15
  • @JonathanWakely: May I ask what is the rationale for not supporting it? It works for `std::pair`, so I think it would be natural to generalize it to `std::tuple`s – Andy Prowl Feb 19 '13 at 16:17
  • I thought I just gave a rationale :) If the desired use case is `return {a,b,c};` I would rather have a more general solution that allows list-initialization to call explicit constructors in return statements, without having to change `std::tuple`. I like having `explicit` constructors, I don't want to have to choose between them and `return {...};`, I want both. – Jonathan Wakely Feb 19 '13 at 16:20
  • @JonathanWakely: You did suggest a generalized solution, which is indeed interesting, but applies only to returning values. Why not allowing *also* the implicit construction of tuples? I see no reason why copy-initialization should fail for `tuple`s and work for `pair`s – Andy Prowl Feb 19 '13 at 16:23
  • 3
    @JonathanWakely: while I agree that the `return` statement is `explicit` enough w.r.t the type to be selected, I still see no reason for the constructor of `std::tuple` to be `explicit` for arities > 1. – Matthieu M. Feb 19 '13 at 16:25
  • `pair` is not the paragon of good design that all things should be compared to, so I don't buy "it works for pair so it should work for tuple" as a good enough argument :) The constructor was marked explicit because you want it to be explicit for the 1-tuple case, AFAIK noone thought to make 1-tuples a special case and have a non-explicit ctor for other tuples until it was too late. – Jonathan Wakely Feb 19 '13 at 16:26
  • As for allowing implicit construction, I don't need it, there's no reason to prefer `tuple<...> a = { ... };` over the `tuple<...> a{...}` form. Uniform init doesn't require the `=`, and if the type isn't movable you _can't_ use the `=`, so it's better to stop using it and live in 2011+ – Jonathan Wakely Feb 19 '13 at 16:28
  • 1
    @JonathanWakely: I'm not taking `pair` as an example of good design, I'm rather questioning why there is such a discrepancy between `pair` and a data structure which should be a generalization of `pair`. I agree the constructor should be explicit for 1-tuples. – Andy Prowl Feb 19 '13 at 16:28
  • If `pair` and `tuple` had been designed at the same time by the same people they probably would have been more consistent (and maybe `pair` would have an explicit constructor!) but they weren't, and they aren't (and it doesn't) – Jonathan Wakely Feb 19 '13 at 16:29
  • 6
    @JonathanWakely: On the other hand if you have a function accepting a tuple (`foo(tuple)`), I would like to be able to call it this way: `foo({3, 3.0, "3"})`. – Andy Prowl Feb 19 '13 at 16:30
  • Yes, I agree that would be useful sometimes. Many of my uses of `tuple` are in variadic templates, where the arity could be zero, one or more, so even if only the 1-tuple constructor was `explicit` I'd need to be explicit, in case the template is instantiated with `sizeof...(Args) == 1`. I'm not strongly against LWG 2051, but I think fixing it in isolation would be unfortunate, as I'd rather see a more general solution that happens to also fix the example in LWG 2051. – Jonathan Wakely Feb 19 '13 at 16:38
  • Making the constructor non-explicit for arities > 1 means that variardic code will work and compile until passed a tuple of arity 0 or 1. Having a way to say "I'm using an explicit constructor here" without having to repeat the typename doesn't have this problem... – Yakk - Adam Nevraumont Feb 19 '13 at 18:18
  • 1
    @Yakk: Yours is an interesting argument, but I think the programmer of the variadic template would have to take this into account: they should know their function could work on tuples on any arity, thus they should construct it explicitly - or rely on the mechanism Jonathan Wakely is proposing. On the other hand, I'd still like to be able to construct a tuple implicitly for passing it as an argument. For me, the two things are not mutually exclusive: the constructor could be made explicit *and* we could have a general way of specifying that we are "using an explicit constructor" – Andy Prowl Feb 19 '13 at 18:23
24

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).

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Is there a way to disable such extensions in libc++, in order to help keep code portable? – bames53 Feb 22 '13 at 19:22
  • Not at this time. However note that the effort to port is a compile-time error followed by a trivial fix. – Howard Hinnant Feb 22 '13 at 19:51
  • Do you maintain a list of libc++ extensions? I didn't see anything in a quick look through the libc++ source and I understand you do a fair bit of experimenting. --- It's true that the once-off porting effort is trivial, but if one is doing continuous integration with build bots on half a dozen platforms it's more convenient to have your work platform flag things. Not that I'm arguing this deserves some special attention; if it were really important libc++ is open source and I could work on this myself... – bames53 Feb 22 '13 at 21:17
  • No, I don't, and that's a good suggestion. Though off the top of my head I can't think of another one besides this one. Oh, `allocator::propagate_on_container_move_assignment` is `true_type`. Sometimes I just get irritated. ;-) Though this one has already made it into the WP for the next standard: http://cplusplus.github.com/LWG/lwg-defects.html#2103 – Howard Hinnant Feb 22 '13 at 21:57
  • 1
    I think it's worth mentioning the [proposal](http://open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3680.html) in the answer. – oblitum May 23 '13 at 22:43
  • ```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.``` Could you explain why this is necessary? If some element cannot be constructed implicitly, it will be a compilation error, be the tuple constructor explicit or not. – Nikolai Aug 19 '13 at 10:28