3

I know that a std::tuple is a valid way to return multiple variables of different types from a function, as mentioned in a talk by Herb Sutter at about 1:31:00 (This code example is mine, not from the video)

auto get_tuple()
{
  return std::make_tuple(1, 2);
}

then "unpacking" it with either

auto t = get_tuple();
int a = std::get<0>(t);
int b = std::get<1>(t);

or

int a;
int b;
std::tie(a, b) = get_tuple();

These methods are great, but I don't particularly like the syntax std::get or std::tie. I wouldn't avoid using a useful tool just because of ugly syntax (in my personal opinion), but what about doing this instead?

auto get_struct()
{
  struct R
  {
    int a;
    int b;
  }

  return R{1, 2};
}

This compiles for me (Visual Studio 2015) and I like the syntax of it much more. I don't want to get accused of prematurely optimizing, but I'll also mention it seems to be many times faster than a tuple. Mostly I just like the syntax though.

That said, I know that just because something compiles doesn't mean the language is meant to be used that way. I'm not exactly sure how to word this question, but basically, is this valid and defined behavior? Can I be confident that this will work on other major compilers? Frankly I was rather surprised that it would deduce the type even when that type is defined in the function itself, so I want to make sure this is okay.

This is the first time I've actually posted on this website, so sorry in advance if I did anything wrong. Please let me know if I did.

EDIT: As pointed out in responses, my claim that a local struct is many times faster than a tuple is completely wrong. I didn't do my test properly. See the replies for more, I just wanted to edit this in to make sure I don't mislead anyone.

Sean Barry
  • 33
  • 5
  • 1
    I agree with the answer below, having classes instead of built-in types will likely make difference in terms of performance. I for one would go getting the arguments like `void get_tuple(int &out1, int &out2);` or even more generic `void get_values(std::vector &values);` - this way to can get any number of values. – Alex Mar 18 '16 at 20:56
  • 1
    Are you sure you are not measuring it in debug mode? With enabled optimizations tuples should not be noticeably slower. – SergeyA Mar 18 '16 at 20:59
  • What do you mean by "it seems to be many times faster than a tuple." What is "it"? – Barry Mar 18 '16 at 21:02
  • @SergeyA You are correct. Rather embarrassingly, I did indeed test in debug mode. Thanks for pointing it out, it would have taken me a while to notice I was in the wrong mode, since I'm building from the command line through another program. (Not VS 2015 directly, just its compiler.) – Sean Barry Mar 18 '16 at 21:35

3 Answers3

4

While the local struct is standard compliant, I advise against using it (as opposed to tuples). First of all, the performance measure is likely incorrect.

Following code:

#include <tuple>

auto get_tuple()
{
  auto tup = std::make_tuple(1.0f, 2.0f);
  return tup;
}

auto get_struct() {
  struct R {
    float a, b;
  };

  return R{1, 2};
}

Produces following ASM in optimized mode:

get_tuple():
        movq    %rdi, %rax
        movl    $0x40000000, (%rdi)
        movl    $0x3f800000, 4(%rdi)
        ret
get_struct():
        movq    .LC3(%rip), %xmm0
        ret
.LC3:
        .quad   4611686019492741120

So it is only two movl instructions longer. I do not think it is posible to measure the effect of this.

And tuple just gives so much more than the struct! You can know it's size in terms of elements (in compile time). You can concatentate tuples. You can build a reflection system on them. You can use them with std::tie. You can provide their type. (With local struct, it would be auto all way down). Nothing like that is available in local structs.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • 1
    `struct` allows a better naming than `get<3>(t)`. (Assuming better names than `a`, `b` ;-) ). If it is for non generic code, I would prefer struct. – Jarod42 Mar 18 '16 at 22:07
3

[...] is this valid and defined behavior?

Yes, you can declare a local class and return an instance of it. That's perfectly fine.

I don't want to get accused of prematurely optimizing, but I'll also mention it seems to be many times faster than a tuple.

I am skeptical that the runtime performance should be any different between a local struct consisting of two ints and std::tuple<int, int>. Compile-time, sure, tuple will be slower.

Since once you return an instance of a local class, it's not really local, you could just make it a non-local class:

struct R { ... };
R get_struct();

Then there's even less question of whether or not that's valid.

Additionally, you could always directly plug your tuple output into something that unpacks it. Check out Yakk's answer for something like:

std::tuple<int, int> get_tuple();

unpack(get_tuple(), [](int i, int j) { ... });
get_tuple() *then* [](int i, int j){ ... };

You may also be happy to know that there's a structured bindings proposal which would directly allow for:

auto {a, b} = get_tuple();
Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977
1

It's valid behavior, and you're correct that it's faster than using a tuple. However, you're using a simple case with built-in types. The performance differences using std::tuple and a struct with user-defined types may make using std::tuple more attractive, despite the less elegant syntax.

TriskalJM
  • 2,393
  • 1
  • 19
  • 20