-1

Let's assume that I have three functions f0, f1, f2 that all take three arguments. Let's also assume that I have a big function bigFunc to call, that takes the return values from the first three functions as arguments.

I may want to make a call like this:

bigFunc(f0(arg0, arg1, arg2), f1(arg3, arg4, arg5), f2(arg6, arg7, arg8));

This is a big call, so I think it would be far more readable to write something like this:

auto bigArg0 = f0(arg0, arg1, arg2);
auto bigArg1 = f1(arg3, arg4, arg5);
auto bigArg2 = f2(arg6, arg7, arg8);

bigFunc(bigArg0, bigArg1, bigArg2);

That's especially great if the names bigArg0, bigArg1, bigArg2 allow me to be more specific about what I am doing (for example if f0, f1, f2 are a bit generic; you can think of STL algorithms, which do different things depending of the type of iterators you give it).

The problem with this, however, is that by naming bigArg0, bigArg1, bigArg2, I make them not to be temporaries anymore, which is (I suppose) harder for the compiler to optimize.

So, here is my question: what is the right way to do this, if I don't want to lose performance? make bigArg0, bigArg1, bigArg2 const? Give the arguments to bigFunc through std::move? A bit of both?

nobody
  • 19,814
  • 17
  • 56
  • 77
Eternal
  • 2,648
  • 2
  • 15
  • 21

2 Answers2

3

Do the destructor and copy-constructor of the return values have observable behavior as the standard defines them?

If they don't and the compiler has all the neccessary info to prove it, the compiler can ellide the copy without needing the RVO exception.

Perhaps that's the case if you use std::move? Either way moving might be better than copying...

Or perhaps bigfunc gets its arguments by constant reference, in which case both ways result in the same code anyway...

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
1

Here's a solution that avoids the named temporaries:

bigFunc(
    f0(arg0, arg1, arg2),
    f1(arg3, arg4, arg5),
    f2(arg6, arg7, arg8)
);

A scaled-up example:

bigFunc(
    f0(
        f0_0(arg0, arg1, arg2),
        f0_1(arg3, arg4, arg5),
        f0_2(arg6, arg7, arg8)
    ),
    f1(
        f1_0(arg9, arg10, arg11),
        f1_1(arg12, arg13, arg14),
        f1_2(arg15, arg16, arg17)
    ),
    f2(
        f2_0(arg18, arg19, arg20),
        f2_1(arg21, arg22, arg23),
        f2_2(arg24, arg25, arg26)
    )
);

This handles 27 arguments and 13 function calls in 17 easy-to-read lines.

That's already lot of stuff to do in one place. By the time this scales up so far that it loses readability, you should start putting parts of it into separate functions and/or combine arguments into structs or classes.

Aberrant
  • 3,423
  • 1
  • 27
  • 38
  • How is that different from what the OP had and **explicitly does not want**? – Deduplicator Jun 27 '14 at 15:22
  • Yeah, well, indenting "works" in this case, but suppose that arg0, arg1, arg2 are obtained through other function calls... your "solution" is not scalable – Eternal Jun 27 '14 at 15:24
  • @Deduplicator by providing the exact format that the OP requested for readability, in stead of the hard-to-read single line. – Aberrant Jun 27 '14 at 15:32
  • @Aberrant: That's taking the example much too literal, especially as the OP commendably used little code to demonstrate his question... – Deduplicator Jun 27 '14 at 15:34
  • @Eternal I've added an example of how this can scale. – Aberrant Jun 27 '14 at 15:38
  • @Deduplicator I really don't see how this does not answer the question. The only thing temporaries have that this format doesn't have is names. Which, if the function names aren't descriptive enough, could be added with a comment at the end of the relevant lines. I don't think I'm being too literal, OP specifically asks for readability without losing performance. Copy/move constructors might not always be optimised away, so this seems to be the safest way to achieve the requested readability. – Aberrant Jun 27 '14 at 15:46
  • In my experience, giving a name to the intermediate values gives quite a lot of readability to the code, because the name works like a comment on the role that the value plays within this part of the code. If you write monster-statements in this style, you are omitting quite a bit of this self commenting information from your code. – cmaster - reinstate monica Jun 27 '14 at 16:35
  • @cmaster Agreed, it's not a silver bullet. But it's still a good solution in many situations. It mostly depends on how well your functions are named. And again, this can be mended by putting comments after function calls. That might sometimes be even better than temporary names, since comments don't lookLikeThis. – Aberrant Jun 27 '14 at 16:42