What's the best way to return a tuple from function in C++11?

13,210

Solution 1

std::forward_as_tuple() creates a tuple of references. Since you are returning a tuple<bool, string, int> anyway, the two end up being equivalent in this case, but I think the first approach is clearer - using forward_as_tuple() when you are not forwarding anything is confusing.

Also, as mentioned by Sebastian Redl in the comments, make_tuple() would allow the compiler to perform copy elision - per paragraph 12.8/31 of the C++11 Standard, while forward_tuple() would not (since what it returns does not have the same type as the function's return type).

Solution 2

I prefer,

std::tuple<bool, std::string, int> f()
{
  ...
  return { false, "home", 0 };
}

EDIT 1

The above code is actually compiling for me under clang/libc++ trunk. As @AndyProwl commented in comments section, this shouldn't since std::tuple constructor is explicit and returning through initialization-list syntax is in copy-initialization context, hence copy-list-initialization, which fails when an explicit constructor is matched.

I don't know the reason why clang/libc++ is passing, I suppose it to be a bug in libc++. Anyway, it's sad one can't do that for tuples...

I think I realized how sad (for me, at last) it's, generally. I was becoming used to that syntax, but one is forced to know beforehand whether or not the returning type contains an explicit constructor anytime for it to work.

EDIT 2

This is indeed a libc++ extension, for more information, checkout Howard Hinnant answer here: https://stackoverflow.com/a/14963014.

It's also currently open in the libc++ bug list: http://llvm.org/bugs/show_bug.cgi?id=15299.

This is the relevant proposal: Daniel Krügler, Improving pair and tuple.

In short this is what happens with libc++:

#include <tuple>
#include <string>

struct S
{
    explicit S(int) {}
};

int main()
{
    std::tuple<int, std::string> t1 = { 1, "hello" }; // ok
    std::tuple<std::string> t2      = "hello";        // ok
    std::tuple<int, S> t3           = { 1, 1 };       // fail: an *element* is to be constructed explicitly
}
Share:
13,210

Related videos on Youtube

Gian Lorenzo Meocci
Author by

Gian Lorenzo Meocci

Updated on May 24, 2020

Comments

  • Gian Lorenzo Meocci
    Gian Lorenzo Meocci about 4 years

    I want to return some values from a function and I want to pack it in a tuple. So I have two possibilities for function declaration:

    std::tuple<bool, string, int> f()
    {
      ...
      return std::make_tuple(false, "home", 0);
    }
    

    and

    std::tuple<bool, string, int> f()
    {
      ...
      return std::forward_as_tuple(false, "home", 0);
    }
    

    These functions are equivalents? Between these functions which do you prefer?

    • Xeo
      Xeo about 11 years
      Are you forwarding those things? No? Use std::make_tuple.
    • Gian Lorenzo Meocci
      Gian Lorenzo Meocci about 11 years
      Ok, it's good too. but witch is real difference from make_tuple and forward_as_tuple?
    • Mooing Duck
      Mooing Duck about 11 years
      @GianLorenzoMeocci: forward_as_tuple is used when you want to forward universal references but as a tuple. If you don't know what that means, don't use it.
  • Sebastian Redl
    Sebastian Redl about 11 years
    Also, more efficient. The first way can possibly use copy elision (if you wrap the string literal in a std::string constructor call), the second can't because it's not a normal copy.
  • Andy Prowl
    Andy Prowl about 11 years
    @SebastianRedl: Correct, worth mentioning. Thank you
  • Andy Prowl
    Andy Prowl about 11 years
    I would also prefer it, but std::tuple has an explicit constructor, meaning the above won't compile
  • Morwenn
    Morwenn about 11 years
    @AndyProwl Let's hope someone will bring N3452 to some future standard.
  • Andy Prowl
    Andy Prowl about 11 years
    @Morwenn: Didn't know about that, thank you. But actually, wouldn't be enough to make the constructor of tuple non-explicit? I don't understand why it has to be so. After all, pair's constructor is not explicit
  • oblitum
    oblitum about 11 years
    @AndyProwl, do you know why this compiles under clang -std=c++11 so?
  • Morwenn
    Morwenn about 11 years
    @AndyProwl Removing explicit from std::tuple constructor would solve the problem for std::tuple. At least, N3452 would solve it for any class in a function return. An no wonder you did not know about it, it does not appear in the mailing.
  • Andy Prowl
    Andy Prowl about 11 years
    @chico: Huh? I tried it, and it does not compile (as I would expect, since the constructor of tuple is explicit)
  • R. Martinho Fernandes
    R. Martinho Fernandes about 11 years
  • Andy Prowl
    Andy Prowl about 11 years
    @chico: That's weird, in the current C++14 draft the constructor is still marked as explicit. I'd say it's a bug in the library implementation.
  • R. Martinho Fernandes
    R. Martinho Fernandes about 11 years
    @AndyProwl Non-explicit variadic constructors also act as implicit conversions when the pack has a single element. I don't see how that would be a problem for tuple though... (And one could always single out that case with some SFINAE)
  • Andy Prowl
    Andy Prowl about 11 years
    @R.MartinhoFernandes: I'm not sure I understand. Are you saying that should compile?
  • R. Martinho Fernandes
    R. Martinho Fernandes about 11 years
    No, not according to the standard. I am just stating one reason why would one might want to make variadic ctors explicit.
  • Matthieu M.
    Matthieu M. about 11 years
    @AndyProwl: actually, it's generally argued that conversion constructors (ie, any constructor that can be invoked with a single argument) should be marked explicit, much like conversion operators can now be marked explicit. However, there was a proposal to lift this rule in the case of return values, arguing that the type was explicit enough since it is spelled out in the function signature. I would guess that clang implemented this later proposal ahead of time.
  • Andy Prowl
    Andy Prowl about 11 years
    @MatthieuM.: That's interesting, I didn't know that. So Clang is also ahead of C++14, cool :)
  • oblitum
    oblitum about 11 years
    This days I'm using clang a lot most of the time with C++11 and I think I've done this more than once without knowing about the issue.
  • R. Martinho Fernandes
    R. Martinho Fernandes about 11 years
    @Andy not cool. That means I using clang+libc++ as my primary compiler if I want portable code can easily get annoying :( I wish compiler/library writers got their act together and made all such extensions opt-in. It's bad enough that I need to pass flags to get the compiler in "real C++" mode. It's much worse if even doing that does not give me the desired result.
  • oblitum
    oblitum about 11 years
    @MatthieuM. it seems not to be the case, I've tested std::tuple copy-list-initialization without involving return from function, it keeps allowing it.
  • Andy Prowl
    Andy Prowl about 11 years
    @R.MartinhoFernandes: Once again I have to agree. I instinctively thought "it's cool" because I've often wanted to return a tuple by list-initialization, and the fact that a proposal exist and a compiler already implements it sounded promising. But you're right, standard compliance should be the priority number one, and if I really want that feature, I should pass a flag enabling that extension
  • Matthieu M.
    Matthieu M. about 11 years
    @chico: good, I was looking into the mail archive and could not find anything about it :) Then it appears it would be a libc++ issue: a missing explicit on the constructor most likely.
  • oblitum
    oblitum about 11 years
    @MatthieuM. In the end, it's a libc++ extension, check updated answer.