CppCoreGuidelines icon indicating copy to clipboard operation
CppCoreGuidelines copied to clipboard

F.41 should suggest returning a struct, not a tuple

Open andyprowl opened this issue 8 years ago • 88 comments

I think F.41 ("Prefer to return tuples to multiple out-parameters") should become "Prefer returning aggregate class types to multiple out-parameters" (or something like that, what I mean is "prefer returning a struct").

Structs have names and their fields have names, which helps understanding and reasoning about code. The variables used in std::tie() have names too, but the user needs to declare those names consistently and meaningfully at every point of use; this is error-prone. Also, those variables must be pre-declared, and two-step initialization is not idiomatic in C++ (and particularly ugly if default-construction is not an option).

Tuples might be a better option for certain kinds of generic code, but I don't see them as a candidate for a default guideline on returning multiple values.

andyprowl avatar Oct 27 '15 12:10 andyprowl

I think these are very valid points.

Tuples are not ideal interface companions. Returning a std::tuple to a std::tie feels like simulating a language ability to return multiple variables. It would be nice to have a little language support to make returning a struct simpler.

Something like this perhaps?

auto func() -> struct{ bool good; int i; std::string text; }
{
    return {true, 6, "yeehar"};
}

// ..

auto ret = func();

if(ret.good)
{
    // use ret.i and ret.text here ...    
}

I actually half expected that syntax to work already but I can't get the compiler to accept an anonymous struct as a trailing return type.

Also returning a struct rather than doing a std::tie facilitates RVO.

galik avatar Oct 27 '15 14:10 galik

In D you can do this:

alias tup = tuple!(bool, "good", int, "i", string, "text");

I am jealous :( This needs at least string mixins and "universal parameters". There is a paper that mentions auto as any non-type parameter. I think we should have extensions to accept both kinds at some point, it is just more flexible.

germandiagogomez avatar Oct 30 '15 09:10 germandiagogomez

I don't know D, but it seems to me this would only solve the two-phase initialization problem. The user still needs to declare those names at every point of use, with potential consequences in terms of consistency, readability, etc.

andyprowl avatar Oct 30 '15 09:10 andyprowl

Thankfully, there is a proposal in the work to allow structured binding.

gdr-at-ms avatar Nov 01 '15 18:11 gdr-at-ms

Thankfully, there is a proposal in the work to allow structured binding.

Where could I find that?

germandiagogomez avatar Nov 02 '15 02:11 germandiagogomez

Here's a better idea than a tuple:why not return an anonymous struct:

auto func()
{
  struct { bool good; int i; std::string text; } var;

  return var;
}

OK, obviously you can't do this if the function isn't inline. But where convenient, a struct is much better than a tuple.

NicolBolas avatar Nov 03 '15 19:11 NicolBolas

It should also be noted that with P0144 (mentioned by Bjarne Stroustrup in his Kona report ), we may have the abiliity in C++17 to do this:

auto {x, y, z} = make_tuple(2, std::string("foo"), 5.4f);

This would obviously work with tuple return values too. Possibly with a user-defined mechanism to allow fetching from arbitrary user objects.

So tuple might be the better answer for ad-hoc multiple return values in the long run.

NicolBolas avatar Nov 15 '15 16:11 NicolBolas

Personally I do not like this solution. Not to this problem at least. It would solve the issue with 2-phase initialization, but it would still require the caller to declare those names at every point of use, which might compromise consistency and readability. Also, reordering tuple members would require changing all points of call.

I'd much rather prefer the solution proposed by Galik earlier in this thread.

andyprowl avatar Nov 15 '15 19:11 andyprowl

For me the conclusion is that we do not have a conclusion on this. Maybe there should be no strong guideline in this case.

germandiagogomez avatar Nov 16 '15 08:11 germandiagogomez

What's wrong with "Prefer returning aggregate class types to multiple out-parameters"? The only disadvantage I see are the extra keystrokes required to define the return type (but that shouldn't be a huge concern, right?); from the viewpoint of readability and maintainability I don't see any major drawback.

andyprowl avatar Nov 16 '15 10:11 andyprowl

What's wrong with "Prefer returning aggregate class types to multiple out-parameters"?

I am not if you say this cause of my last comment. What I meant, anyway, the tuple vs struct debate did not finish with a real conclusion. I did not mean the out parameters at all. But I am not sure you say this because of my last comment.

germandiagogomez avatar Nov 16 '15 11:11 germandiagogomez

I honestly don't see any benefits to returning a tuple rather than a struct. There maybe some corner-cases when you happen to have the right variable laying around, otherwise you have to define them individually and even then there is always room to std::tie the wrong variable.

With a struct you get to follow the always initialize a variable rule that using std::tie breaks.

struct func_ret_type { bool success; int i; };
func_ret_type func(); // maybe keep struct defn next to func prototype
// ...
func_ret_type ret = func(); // follows always initialize rule (ES.20)

// seems better than

bool success; // not initialized
int i; // not initialized
std::tie(success, i) = func(); // complex initialization frowned upon (ES.20)

// or worse

bool success; // not initialized
int i; // not initialized
std::tie(success, j) = func(); // whoops wrong variable

There is some discussion regarding the always initialize rule (ES.20) here: https://github.com/isocpp/CppCoreGuidelines/issues/131

galik avatar Nov 16 '15 11:11 galik

@galik: Well, we were discussing the new proposal brought up by Nicol Bolas. Sure, std::tie and two-phase initialization are definitely not what I'd like to see in a guideline, but P0144 would solve this problem. The issue I see is that it does not solve the main problem - consistent naming, sensitivity to reordering of members, etc. Tuples are convenient when you want positional access. I just don't think you'd want that very often. IME it is a use case that comes up fairly frequently with generic code, but that's not enough to make it a default guideline.

@germandiagogomez: Yes, I wrote that because of your last comment. Personally I think there should be a strong guideline, and it should be "Prefer returning aggregate class types to multiple out-parameters".

andyprowl avatar Nov 16 '15 13:11 andyprowl

@andyprowl

The issue I see is that it does not solve the main problem - consistent naming, sensitivity to reordering of members, etc.

The thing is, it's usually pretty obvious when you want to return multiple values as multiple values and when you want to return a struct as a struct. Nobody in their right mind would return a tuple<float, float, float> when they really meant fvec3d. A vector in this case is a conceptual value, whereas multiple return values are multiple independent values who's only association is that one function happens to return all of them.

Positional sensitivity is something we already have with function parameters, so function output values is not far afield. If we can deal with function arguments by position, I see no problem with dealing with return values by position.

NicolBolas avatar Nov 16 '15 19:11 NicolBolas

Nobody in their right mind would return a tuple<float, float, float> when they really meant fvec3d. A vector in this case is a conceptual value, whereas multiple return values are multiple independent values who's only association is that one function happens to return all of them.

What if you use something with a conceptual background but you only use it a few times in all app? Are you going to write all boilerplate for the class? I guess no. So that would contradict what you just said.

germandiagogomez avatar Nov 17 '15 03:11 germandiagogomez

A vector in this case is a conceptual value, whereas multiple return values are multiple independent values who's only association is that one function happens to return all of them.

I'm not sure I can come up with a good example of such a situation. IME when a function needs to return several pieces of information, then those pieces of information are somewhat related to each other in a tighter way than just happening to be returned by the same function. If that were not the case, I'd probably be wondering if the function isn't violating SRP.

This might just be me, but for instance I'd prefer certain standard algorithms to return a structure with "found" and "element" data members rather than a pair of a bool and an iterator. Do these count as "multiple independent values"? If not, can you come up with a better example?

Positional sensitivity is something we already have with function parameters, so function output values is not far afield. If we can deal with function arguments by position, I see no problem with dealing with return values by position.

This is a valid point. However, my personal opinion is that being able to initialize arguments by name would also be nice :)

andyprowl avatar Nov 18 '15 10:11 andyprowl

I'm not sure I can come up with a good example of such a situation. IME when a function needs to return several pieces of information, then those pieces of information are somewhat related to each other in a tighter way than just happening to be returned by the same function.

stoi and its ilk offer a great example of this. These functions can conceptually return three values:

  1. The number taken from the string.
  2. The character offset from the start of the string to the character just past the number parsed
  3. Whether they failed to find a number or not.

1 and 2 don't have much to do with one another. Yet it would be very silly indeed to have two separate functions returning this value. stoi had to compute 2 in order to do 1, so it makes perfect sense to provide it as a return value.

The question is not whether the data are "related;" it's whether they're conceptually coupled together. In the stoi example, 1 and 2 are not used together in the same way that you use the elements of a vec3d together. You don't use the aggregate of them as a logical unit. You don't pass that aggregate around to other people. You'll pass the parsed number around, but you'll probably apply that offset yourself, with nobody else using it.

3 has to do with 1 and 2, but only in the sense that 1 and 2 cannot exist unless 3 reports success.

The ideal return value would be something like optional<tuple<T, size_t>>. There is simply no need to name these values, to make some template struct that contains T number; and size_t pos fields. A tuple works just fine.

To me, the telling difference between tuple and struct as return values is this: is the aggregate part of my design, with interfaces that take this aggregate and so forth? Or do I just need a quick, one-time aggregate of a couple of values, which will almost immediately be unbundled and used separately? Am I returning a logical construct or just working around C++'s inability to return multiple values?

This might just be me, but for instance I'd prefer certain standard algorithms to return a structure with "found" and "element" data members rather than a pair of a bool and an iterator. Do these count as "multiple independent values"?

The values are not independent, since the iterator has no meaningful value unless the "found" is true. This is a job for optional, a type who's sole purpose is to represent whether or not a thing exists.

NicolBolas avatar Nov 18 '15 15:11 NicolBolas

Thanks @andyprowl for bringing up this point, and the nice discussion that follows due to this.

I just wanted to make a case for using std::tuple as a return type. I do think this is a fine default guideline, to use this as a first choice over std::pair or std::vector<some_variant> or whatever. Making an aggregate class for every function return by default would result in too many aggregate classes, which I find hard giving sensible names (my_function_data, that_function_values). I do notice in my own code that I start with std::tuple (thanks to rule F.41) and move to full classes when the code is evolving and I need more control, especially when there are more than two values that need to be returned. Then, I can give the returned values sensible names as well (gradients, diffusions).

Keep up the good work!

richelbilderbeek avatar Jan 16 '16 10:01 richelbilderbeek

I agree with @richelbilderbeek. Many languages support mulitple return values via tuple. It avoids the overhead of making a struct everytime you need an ad hoc return value. It would be very nice if the language supported something to do this. I think, for example, Go has a very nice implementation of this feature.

I feel like the guideline should be "if you are only making a struct to return multiple values, prefer a tuple."

Multiple out parameters have negative side effects in many cases, and interfere with the recommendation to prefer value semantics.

nadiasvertex avatar Jan 16 '16 16:01 nadiasvertex

Is returning multiple value without the context and meaning of a clear and known struct really a good idea ?

Pazns avatar Jan 16 '16 16:01 Pazns

I would just like to compare what, for me, might be a typical use scenario where one of the return values from the function is an error indicator (true or false). This, I believe, might be the majority use case for many projects.

The benefits of using a struct here seem large compared with a tuple. A tuple will pollute the scope with essentially temporary variable declarations, runs the risk of initializing the wrong variable and violates the always initialize rule.

// more succinct declaration
std::tuple<bool, int, double> return_tuple_stuff();

// less succinct return statement
// { return std::make_tuple(true, 1, 1.0); }

// pollute larger scope with temporary variable names
// no opportunity to restrict entire scope
// declare variable names every time used
// may initialize wrong variables
// violates the 'always initialize' rule
// No RVO
bool success;
int i;
double d;
std::tie(success, i, d) = return_tuple_stuff();

if(success)
{
    // use i, d here
}

// still have the variables success, i and d hanging round here...

The struct version has none of these problems:

// less succinct declaration
struct return_struct_stuff_rv
{
    bool success;
    int i;
    double d;
    operator bool() const { return success; }
};

return_struct_stuff_rv return_struct_stuff();

// more succinct return statement
// { return {true, 1, 1.0}; }

// variable names restricted to struct namespace
// potential to restrict scope of all values
// declare variables once at function declaration/definition
// impossible to initialize the wrong variable
// follows the 'always initialize' rule
// RVO makes this very efficient
if(auto rv = return_struct_stuff())
{
    // use rv.i, rv.d here
}

// no cruft polluting the rest of the code here

I understand that P0144 would solve initializing the wrong variable problem as well as fixing the always initialize problem and possibly allow for RVO but none of the other problems are addressed as far as I understand the proposal. The struct version is a little heavier on the declaration but with the benefit of being very light and unobtrusive every time the function is called.

The tuple version and even the P0144 proposal seem rather 'clunky' in comparison at the point the function is called.

galik avatar Jan 16 '16 17:01 galik

I actually think P0144 is solving the problem completely the wrong way. A struct is the natural way to provide aggregate data and I would think it would be much simpler allow anonymous struct declarations as a natural extension to the return type than to add more complex exotic syntax to initialize multiple variable at a time.

galik avatar Jan 16 '16 18:01 galik

@galik: A status value that changes the meaning of other results from a function is basically what P0157 is surveying.

For your given example, where the return value is useless if the function failed, but you don't want to use exceptions for that, something like

Expected<std::tuple<int,double>, std::experimental::nullopt_t> return_stuff()
{ 
 //...
 if (succeeded){
  return make_tuple( 1, 1.0 ); // or with C++17, return { 1, 1.0 }; I believe?
 } else {
  return make_unexpected( false );
 }
}

/// ...

if(auto rv = return_stuff()) {
 int i;
 double d;
 std::tie(i, d) = *rv;
 // Do stuff with i, d
}

TBBle avatar Jan 17 '16 05:01 TBBle

@TBBle : What are, in the end, the real benefits against C-like approach, like returning error code and returned values through parameter side-effects ?

If you often return this coherent tuple, doesn't it may be a well known struct instead ? If you often return multiple values (with no cohesion between them) at the same time with a bunch telling the meaning of others, are you really following a good programming pattern ?

Pazns avatar Jan 17 '16 16:01 Pazns

@Pazns

What are, in the end, the real benefits against C-like approach, like returning error code and returned values through parameter side-effects ?

That's not the "C-like approach"; indeed, there is no such approach. C has many different ways of dealing with errors. Some APIs return errors as output parameters; others return errors as return values. Still others use a mechanism like errno to "return" the error completely outside of the function call.

The biggest issue with output parameters is already spelled out in the guidelines: they requires you to create an object and put it in a valid state, even if it's not really going to get filled in. It's a form of 2-state construction, and we don't like that in C++.

Consider std::list. Some implementations allocate memory in the default constructor (which is why it is not noexcept). Now, why should I construct such an object, then call someone to fill it in for me, if the function that fills it in may fail? The natural way to express such a function is for it to return std::list.

Therefore expected<list, error_code> makes far more sense as a way to express the concept that a function either generates the value or returns an error code. If a function uses an output parameter, it's less clear what the state of that parameter will be if the function errors. With an expected value, it's very clear: the value type was never created at all.

If you often return this coherent tuple, doesn't it may be a well known struct instead ?

Yes, it should.

If you often return multiple values (with no cohesion between them) at the same time with a bunch telling the meaning of others, are you really following a good programming pattern ?

Who is to say what is "good" or "bad"? Time once was we thought that returning things by value was a bad programming pattern.

Patterns change based on the syntax that a language provides. Languages that have multiple return values as a first class feature sees them used semi-frequently.

See, the biggest problem to me with returning a tuple is not the semantics. In real-world examples, you can usually figure out what the values mean from the name of the functions and the name of the return types.

The biggest problem to me with returning a tuple is that they are painful to use. Accessing the values from a tuple requires huge, bloated syntax. Structured binding takes that way, and therefore makes the tactic much more legitimate.

And once it becomes easier to use, maybe people will suddenly find themselves writing functions that naturally return multiple values. If it's easy to return them and easy to use them, people may start doing it more.

If you build syntax, people will use it. What you need to explain is why people shouldn't use it, even if the syntax is there.

NicolBolas avatar Jan 17 '16 19:01 NicolBolas

Therefore expected<list, error_code> makes far more sense as a way to express the concept that a function either generates the value or returns an error code.

But if you want to return an error code and an object ? And what is the state of the value part of the Expected object if there is an error ? Isn't this the same thing than with output parameters being undefined, pragmatically ?

Seems the same thing all over again, but with less flexibility.

Pazns avatar Jan 17 '16 20:01 Pazns

If you want to return a status code and an object, that's also part of P0157's survey. In that case, the other current proposal, status_value (N4233) works roughly the same way, except that the 'status' value is always filled in, and is independent of the value.

It was motivated by a concurrent queue proposal, with the idea that 'try_pop' and 'nonblocking_pop' can return an object or not, but either way there may be useful information that can also be returned. The example mooted is 'contention status', but the status from the motivating example are:

  • success (object returned)
  • empty (cannot pop)
  • full (cannot push)
  • busy (non-blocking call would have blocked)

so Expected would handle this rationally.

The status_code use-case I recall (vaguely) from a presentation was "post-pop status", so as well as whether you got an object or not, you can also know whether there's more objects to get.


As far as the state of the value part of Expected, you can consider it as basically variant<T,E>, with more accessors since there's more semantic information than just "Could be a T or an E".

Anyway, I think this is getting off-topic and too detailed for this discussion, beyond the idea that I don't "different return value states" is a strong motivating example for recommending a struct here, as there's better mechanisms (with specific semantics) in the pipeline.

TBBle avatar Jan 18 '16 00:01 TBBle

On Sun, Jan 17, 2016 at 11:17 AM Pazns [email protected] wrote:

@TBBle https://github.com/TBBle : What are, in the end, the real benefits against C-like approach, like returning error code and returned values through parameter side-effects ?

If you often return this coherent tuple, doesn't it may be a well known struct instead ? If you often return multiple values (with no cohesion between them) at the same time with a bunch telling the meaning of others, are you really following a good programming pattern ?

It depends on what you are talking about. It is very, very common to want to return an error code and something else. So okay, that can be dealt with directly as explained by others.

However, it is also not terribly unusual to want to return a couple of items. Think about progressive string tokenizing where you want to return the token and the next start position. The items are related contextually, but that doesn't mean you want the overhead of a struct.

Out parameters are generally a bad idea semantically. It can be very unclear who owns what, and they are also frequently opaque to the optimizer. There's very little real argument against favoring value semantics.

nadiasvertex avatar Jan 18 '16 01:01 nadiasvertex

@nadiasvertex It may be worth mentioning that a struct should not introduce any overhead. In fact using tuple & tie as is currently suggested will likely have greater overhead because you lose the opportunity for RVO by having to declare your variables before assigning them a new value with the returned tuple. A struct, on the other hand, should have its copy completely elided.

galik avatar Jan 18 '16 02:01 galik

@galik:

In fact using tuple & tie as is currently suggested will likely have greater overhead because you lose the opportunity for RVO by having to declare your variables before assigning them a new value with the returned tuple.

How true these are is a matter of language evolution.

If you say return {value1, value2};, the standard currently guarantees that this will initialize the return value in-place (and thus achieve elision). With tuple, you can't say that because tuple's constructor is explicit. So you have to use return make_tuple(value1, value2);, which does not guarantee elision.

Of course, you'll still almost certainly get it. But you aren't guaranteed it.

The committee already voted in a change to tuple, allowing the constructor to not be explicit if all of the types are implicitly convertible from the given types. Equally importantly, C++17 will also add changes that will make constructs like return make_tuple(value1, value2) guarantee elision. At least, they guarantee it on the sender's side.

As for the receiving side, you are correct that tie guarantees that you won't get elision. However, the P0144 proposal for structured binding (PDF) says that its intent is to still allow you to have elision with structured binding.

So with the proper language features, elision should be a non-issue in this regard.

NicolBolas avatar Jan 18 '16 04:01 NicolBolas

Maybe I've overlooked something, but before P0135R0, I don't believe there was any guarantee of copy elision. return {value1, value2}; constructs a temporary there and then returns it, which is the same thing make_tuple does, although the latter needs to chain two such copy-elisions to fully elide the copy or move.

So as far as I understood, any modern compiler should expect to elide both of them.

If you want to fully-elide with the tuple, don't use tie, just receive the returned tuple by value.

TBBle avatar Jan 18 '16 10:01 TBBle

Brace initialization in a return statement actually constructs the returned object directly (rather than copy- or move-constructing it from a temporary), so one copy/move is guaranteed to be elided. There is no guarantee on the other end though (yet). On Jan 18, 2016 11:31, "TBBle" [email protected] wrote:

Maybe I've overlooked something, but before P0135R0 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html, I don't believe there was any guarantee of copy elision. return {value1, value2}; constructs a temporary there and then returns it, which is the same thing make_tuple does, although the latter needs to chain two such copy-elisions to fully elide the copy or move.

So as far as I understood, any modern compiler should expect to elide both of them.

— Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172491849 .

andyprowl avatar Jan 18 '16 10:01 andyprowl

@nadiasvertex :

However, it is also not terribly unusual to want to return a couple of items. Think about progressive string tokenizing where you want to return the token and the next start position. The items are related contextually, but that doesn't mean you want the overhead of a struct.

I must admit I have difficulties, and probably lack of experience, to visualize a real, unavoidable, use case where you want to return in the same call multiple things that are both non strongly coupled (justifying the lack of a durable stateful struct) nor available through smaller, independent, methods.

For your example with a tokenized string, one could use an Iterator pattern, which achieve this without complicated and heavy variant<> or Expected<>, or I may have missed something.

Pazns avatar Jan 18 '16 12:01 Pazns

@pazns An iterator might be a good choice for my example in some situations. In other situations it is not. For example, consider a recursive-descent parser. There are many different pieces of code which are interested in processing tokens. There is also a desire to backtrack to some state, etc. I will grant you that there are many ways of solving this particular problem, but the point is that returning multiple values is a very useful thing. The vast majority of significant new languages in the last 5 years support MRV.

On Mon, Jan 18, 2016 at 7:32 AM Pazns [email protected] wrote:

@nadiasvertex https://github.com/nadiasvertex :

However, it is also not terribly unusual to want to return a couple of items. Think about progressive string tokenizing where you want to return the token and the next start position. The items are related contextually, but that doesn't mean you want the overhead of a struct.

I must admit I have difficulties, and probably lack of experience, to visualize a real, unavoidable, use case where you want to return in the same call multiple things that are both non strongly coupled (justifying the lack of a durable stateful struct) nor available through smaller, independent, methods. For your example with a tokenized string, one could use an Iterator pattern, which achieve this without complicated and heavy variant<> or Expected<>, or I may have missed something.

— Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172516617 .

nadiasvertex avatar Jan 18 '16 13:01 nadiasvertex

The vast majority of significant new languages in the last 5 years support MRV.

I don't think it's fair to say that C++ doesn't support multiple return values; it does and so does C. In C++ you have more options than in C because you can return std::tuple, std::pair or even the newer variant, optional and so-on.

The question here is what is the recommended best practice idiom for returning multiple values. So far, it seems that the general consensus is that pointer or reference arguments (e.g. the C way) are the least desirable of the alternatives.

LegalizeAdulthood avatar Jan 18 '16 19:01 LegalizeAdulthood

IMO, struct { ... } foo(); would be the best except that it seems to be disallowed by the standards. Why? Perhaps so compilers would be forced to reject code where the trailing semicolon was forgotten for a struct/class declaration? (A web search for "c++ new types may not be defined in a return type" indicates that mistake seems to be common...)

struct {
  int a;
  foo b;
} f()
{
  return { 42, foo('x') };
}

decltype(f()) g()
{
  auto rv = f();
  ++rv.a;
  return rv;
}

MichaelCook avatar Jan 18 '16 20:01 MichaelCook

I agree with @richelbilderbeek and @nadiasvertex for the reasons they stated in favor of Tuple. Tuple is the more modern way of handling this facilitating mutability and better serialization/de-serialization with other languages.

CharlesBunders avatar Jan 18 '16 21:01 CharlesBunders

Was disallowed in C++, long before we had a Standard. Good!

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 12:36 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

IMO, struct { ... } foo(); would be the best except that it seems to be disallowed by the standards. Why? Perhaps so compilers would be forced to reject code where the trailing semicolon was forgotten for a struct/class declaration? (A web search for "c++ new types may not be defined in a return type" indicates that mistake seems to be common...)

struct { int a; foo b; } f() { return { 42, foo('x') }; }

decltype(f()) g() { auto rv = f(); ++rv.a; return rv; }

Reply to this email directly or view it on GitHubhttps://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172647082.

gdr-at-ms avatar Jan 18 '16 21:01 gdr-at-ms

Gabi, would you mind commenting on why is that good? I understand you guys have a difficult task, but this kind of intervention isn't very useful for educational purposes.

I myself believe Michael's approach would make returning multiple values much cleaner than using tuples and binding in certain cases. No need to redeclare names at every call point etc. I made these observations already, I won't bother you further.

But I would like to understand why defining the return type "inline" would be a bad thing, because it isn't clear to me. On Jan 18, 2016 10:09 PM, "Gabriel Dos Reis" [email protected] wrote:

Was disallowed in C++, long before we had a Standard. Good!

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 12:36 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

IMO, struct { ... } foo(); would be the best except that it seems to be disallowed by the standards. Why? Perhaps so compilers would be forced to reject code where the trailing semicolon was forgotten for a struct/class declaration? (A web search for "c++ new types may not be defined in a return type" indicates that mistake seems to be common...)

struct { int a; foo b; } f() { return { 42, foo('x') }; }

decltype(f()) g() { auto rv = f(); ++rv.a; return rv; }

Reply to this email directly or view it on GitHub< https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172647082

.

— Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172654197 .

andyprowl avatar Jan 18 '16 21:01 andyprowl

Introducing multiple toplevel "entities" in one declaration isn't something we recommend.

But this specific ban has been in C++ long before there was C++.

It does not look like we're going back. So we can "discuss" it, but it does not look like we are going back.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:28 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Gabi, would you mind commenting on why is that good? I understand you guys have a difficult task, but this kind of intervention isn't very useful for educational purposes.

I myself believe Michael's approach would make returning multiple values much cleaner than using tuples and binding in certain cases. No need to redeclare names at every call point etc. I made these observations already, I won't bother you further.

But I would like to understand why defining the return type "inline" would be a bad thing, because it isn't clear to me. On Jan 18, 2016 10:09 PM, "Gabriel Dos Reis" [email protected] wrote:

Was disallowed in C++, long before we had a Standard. Good!

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 12:36 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

IMO, struct { ... } foo(); would be the best except that it seems to be disallowed by the standards. Why? Perhaps so compilers would be forced to reject code where the trailing semicolon was forgotten for a struct/class declaration? (A web search for "c++ new types may not be defined in a return type" indicates that mistake seems to be common...)

struct { int a; foo b; } f() { return { 42, foo('x') }; }

decltype(f()) g() { auto rv = f(); ++rv.a; return rv; }

Reply to this email directly or view it on GitHub< https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172647082

.

Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172654197 .

Reply to this email directly or view it on GitHubhttps://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172658607.

gdr-at-ms avatar Jan 18 '16 21:01 gdr-at-ms

Ok, I know I'm gonna sound overly polemic, which I don't want, but I really can't help it: "we are not going back", "we do not recommend it" and "the ban has always been there" are not counter-arguments.

I'm not saying there aren't counter arguments, mind you, but I would like to hear them. Why is "going back" (more like, "generalizing the current rules") not an option, if it makes things cleaner? On Jan 18, 2016 10:33 PM, "Gabriel Dos Reis" [email protected] wrote:

Introducing multiple toplevel "entities" in one declaration isn't something we recommend.

But this specific ban has been in C++ long before there was C++.

It does not look like we're going back. So we can "discuss" it, but it does not look like we are going back.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:28 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Gabi, would you mind commenting on why is that good? I understand you guys have a difficult task, but this kind of intervention isn't very useful for educational purposes.

I myself believe Michael's approach would make returning multiple values much cleaner than using tuples and binding in certain cases. No need to redeclare names at every call point etc. I made these observations already, I won't bother you further.

But I would like to understand why defining the return type "inline" would be a bad thing, because it isn't clear to me. On Jan 18, 2016 10:09 PM, "Gabriel Dos Reis" [email protected] wrote:

Was disallowed in C++, long before we had a Standard. Good!

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 12:36 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

IMO, struct { ... } foo(); would be the best except that it seems to be disallowed by the standards. Why? Perhaps so compilers would be forced to reject code where the trailing semicolon was forgotten for a struct/class declaration? (A web search for "c++ new types may not be defined in a return type" indicates that mistake seems to be common...)

struct { int a; foo b; } f() { return { 42, foo('x') }; }

decltype(f()) g() { auto rv = f(); ++rv.a; return rv; }

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172647082

.

Reply to this email directly or view it on GitHub < https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172654197

.

Reply to this email directly or view it on GitHub< https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172658607

.

— Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172659947 .

andyprowl avatar Jan 18 '16 21:01 andyprowl

Why and how does it make things cleaner?

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:42 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Ok, I know I'm gonna sound overly polemic, which I don't want, but I really can't help it: "we are not going back", "we do not recommend it" and "the ban has always been there" are not counter-arguments.

I'm not saying there aren't counter arguments, mind you, but I would like to hear them. Why is "going back" (more like, "generalizing the current rules") not an option, if it makes things cleaner? On Jan 18, 2016 10:33 PM, "Gabriel Dos Reis" [email protected] wrote:

Introducing multiple toplevel "entities" in one declaration isn't something we recommend.

But this specific ban has been in C++ long before there was C++.

It does not look like we're going back. So we can "discuss" it, but it does not look like we are going back.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:28 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Gabi, would you mind commenting on why is that good? I understand you guys have a difficult task, but this kind of intervention isn't very useful for educational purposes.

I myself believe Michael's approach would make returning multiple values much cleaner than using tuples and binding in certain cases. No need to redeclare names at every call point etc. I made these observations already, I won't bother you further.

But I would like to understand why defining the return type "inline" would be a bad thing, because it isn't clear to me. On Jan 18, 2016 10:09 PM, "Gabriel Dos Reis" [email protected] wrote:

Was disallowed in C++, long before we had a Standard. Good!

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 12:36 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

IMO, struct { ... } foo(); would be the best except that it seems to be disallowed by the standards. Why? Perhaps so compilers would be forced to reject code where the trailing semicolon was forgotten for a struct/class declaration? (A web search for "c++ new types may not be defined in a return type" indicates that mistake seems to be common...)

struct { int a; foo b; } f() { return { 42, foo('x') }; }

decltype(f()) g() { auto rv = f(); ++rv.a; return rv; }

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172647082

.

Reply to this email directly or view it on GitHub < https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172654197

.

Reply to this email directly or view it on GitHub< https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172658607

.

Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172659947 .

Reply to this email directly or view it on GitHubhttps://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172661745.

gdr-at-ms avatar Jan 18 '16 21:01 gdr-at-ms

@MichaelCook I agree with you completely. The thing is the syntax is largely there already because of the dreaded:

typedef struct {} mytype;

I would guess it would be rather easy to enable the compiler to accept the same thing when declaring a function return type.

I must admit I am not really sure why so many people seem eager to drop the struct for aggregate data on the return type in favour of introducing yet more exotic syntax to support the tuple. For all the reasons I have outlined. Perhaps the tuple method simply has traction in people's minds now and so momentum will carry that forward but I have not yet been convinced of any real technical or aesthetic benefits over the faithful struct.

galik avatar Jan 18 '16 21:01 galik

It would make things cleaner because the user of a function would know from its declaration what the returned values/objects are, and what they mean: that's because they would have names.

With tuples and binding, those names would have to be redeclared at each point of call, which potentially opens consistency issues. It also forces to modify all points of call if the order of the elements in the tuple changes.

These points have been made already in this thread, so I'm not going to repeat them. Basically, my point is that while returning tuples with binding might be a useful idiom in certain situations (IIRC Nicol Bolas did provide a reasonable example), it is not always the cleanest solution. In fact, IMO it is not the cleanest solution in most cases, but that's of course subjective.

I would like to hear a strong argument against the in-place definition of the return type. On Jan 18, 2016 10:47 PM, "Gabriel Dos Reis" [email protected] wrote:

Why and how does it make things cleaner?

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:42 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Ok, I know I'm gonna sound overly polemic, which I don't want, but I really can't help it: "we are not going back", "we do not recommend it" and "the ban has always been there" are not counter-arguments.

I'm not saying there aren't counter arguments, mind you, but I would like to hear them. Why is "going back" (more like, "generalizing the current rules") not an option, if it makes things cleaner? On Jan 18, 2016 10:33 PM, "Gabriel Dos Reis" [email protected] wrote:

Introducing multiple toplevel "entities" in one declaration isn't something we recommend.

But this specific ban has been in C++ long before there was C++.

It does not look like we're going back. So we can "discuss" it, but it does not look like we are going back.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:28 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Gabi, would you mind commenting on why is that good? I understand you guys have a difficult task, but this kind of intervention isn't very useful for educational purposes.

I myself believe Michael's approach would make returning multiple values much cleaner than using tuples and binding in certain cases. No need to redeclare names at every call point etc. I made these observations already, I won't bother you further.

But I would like to understand why defining the return type "inline" would be a bad thing, because it isn't clear to me. On Jan 18, 2016 10:09 PM, "Gabriel Dos Reis" [email protected] wrote:

Was disallowed in C++, long before we had a Standard. Good!

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 12:36 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

IMO, struct { ... } foo(); would be the best except that it seems to be disallowed by the standards. Why? Perhaps so compilers would be forced to reject code where the trailing semicolon was forgotten for a struct/class declaration? (A web search for "c++ new types may not be defined in a return type" indicates that mistake seems to be common...)

struct { int a; foo b; } f() { return { 42, foo('x') }; }

decltype(f()) g() { auto rv = f(); ++rv.a; return rv; }

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172647082

.

Reply to this email directly or view it on GitHub <

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172654197

.

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172658607

.

Reply to this email directly or view it on GitHub < https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172659947

.

Reply to this email directly or view it on GitHub< https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172661745

.

— Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172662459 .

andyprowl avatar Jan 18 '16 21:01 andyprowl

What do you consider a strong argument?

The ask is so we can get to the chase.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 3:56 PM (GMT-06:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

It would make things cleaner because the user of a function would know from its declaration what the returned values/objects are, and what they mean: that's because they would have names.

With tuples and binding, those names would have to be redeclared at each point of call, which potentially opens consistency issues. It also forces to modify all points of call if the order of the elements in the tuple changes.

These points have been made already in this thread, so I'm not going to repeat them. Basically, my point is that while returning tuples with binding might be a useful idiom in certain situations (IIRC Nicol Bolas did provide a reasonable example), it is not always the cleanest solution. In fact, IMO it is not the cleanest solution in most cases, but that's of course subjective.

I would like to hear a strong argument against the in-place definition of the return type. On Jan 18, 2016 10:47 PM, "Gabriel Dos Reis" [email protected] wrote:

Why and how does it make things cleaner?

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:42 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Ok, I know I'm gonna sound overly polemic, which I don't want, but I really can't help it: "we are not going back", "we do not recommend it" and "the ban has always been there" are not counter-arguments.

I'm not saying there aren't counter arguments, mind you, but I would like to hear them. Why is "going back" (more like, "generalizing the current rules") not an option, if it makes things cleaner? On Jan 18, 2016 10:33 PM, "Gabriel Dos Reis" [email protected] wrote:

Introducing multiple toplevel "entities" in one declaration isn't something we recommend.

But this specific ban has been in C++ long before there was C++.

It does not look like we're going back. So we can "discuss" it, but it does not look like we are going back.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:28 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Gabi, would you mind commenting on why is that good? I understand you guys have a difficult task, but this kind of intervention isn't very useful for educational purposes.

I myself believe Michael's approach would make returning multiple values much cleaner than using tuples and binding in certain cases. No need to redeclare names at every call point etc. I made these observations already, I won't bother you further.

But I would like to understand why defining the return type "inline" would be a bad thing, because it isn't clear to me. On Jan 18, 2016 10:09 PM, "Gabriel Dos Reis" [email protected] wrote:

Was disallowed in C++, long before we had a Standard. Good!

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 12:36 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

IMO, struct { ... } foo(); would be the best except that it seems to be disallowed by the standards. Why? Perhaps so compilers would be forced to reject code where the trailing semicolon was forgotten for a struct/class declaration? (A web search for "c++ new types may not be defined in a return type" indicates that mistake seems to be common...)

struct { int a; foo b; } f() { return { 42, foo('x') }; }

decltype(f()) g() { auto rv = f(); ++rv.a; return rv; }

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172647082

.

Reply to this email directly or view it on GitHub <

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172654197

.

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172658607

.

Reply to this email directly or view it on GitHub < https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172659947

.

Reply to this email directly or view it on GitHub< https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172661745

.

Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172662459 .

Reply to this email directly or view it on GitHubhttps://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172664797.

gdr-at-ms avatar Jan 18 '16 22:01 gdr-at-ms

A strong argument would be one that explains why banning a feature that makes things cleaner (for the reasons I outlined in my previous message) is considered "good". On Jan 18, 2016 11:14 PM, "Gabriel Dos Reis" [email protected] wrote:

What do you consider a strong argument?

The ask is so we can get to the chase.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 3:56 PM (GMT-06:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

It would make things cleaner because the user of a function would know from its declaration what the returned values/objects are, and what they mean: that's because they would have names.

With tuples and binding, those names would have to be redeclared at each point of call, which potentially opens consistency issues. It also forces to modify all points of call if the order of the elements in the tuple changes.

These points have been made already in this thread, so I'm not going to repeat them. Basically, my point is that while returning tuples with binding might be a useful idiom in certain situations (IIRC Nicol Bolas did provide a reasonable example), it is not always the cleanest solution. In fact, IMO it is not the cleanest solution in most cases, but that's of course subjective.

I would like to hear a strong argument against the in-place definition of the return type. On Jan 18, 2016 10:47 PM, "Gabriel Dos Reis" [email protected] wrote:

Why and how does it make things cleaner?

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:42 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Ok, I know I'm gonna sound overly polemic, which I don't want, but I really can't help it: "we are not going back", "we do not recommend it" and "the ban has always been there" are not counter-arguments.

I'm not saying there aren't counter arguments, mind you, but I would like to hear them. Why is "going back" (more like, "generalizing the current rules") not an option, if it makes things cleaner? On Jan 18, 2016 10:33 PM, "Gabriel Dos Reis" [email protected] wrote:

Introducing multiple toplevel "entities" in one declaration isn't something we recommend.

But this specific ban has been in C++ long before there was C++.

It does not look like we're going back. So we can "discuss" it, but it does not look like we are going back.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:28 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Gabi, would you mind commenting on why is that good? I understand you guys have a difficult task, but this kind of intervention isn't very useful for educational purposes.

I myself believe Michael's approach would make returning multiple values much cleaner than using tuples and binding in certain cases. No need to redeclare names at every call point etc. I made these observations already, I won't bother you further.

But I would like to understand why defining the return type "inline" would be a bad thing, because it isn't clear to me. On Jan 18, 2016 10:09 PM, "Gabriel Dos Reis" <[email protected]

wrote:

Was disallowed in C++, long before we had a Standard. Good!

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 12:36 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

IMO, struct { ... } foo(); would be the best except that it seems to be disallowed by the standards. Why? Perhaps so compilers would be forced to reject code where the trailing semicolon was forgotten for a struct/class declaration? (A web search for "c++ new types may not be defined in a return type" indicates that mistake seems to be common...)

struct { int a; foo b; } f() { return { 42, foo('x') }; }

decltype(f()) g() { auto rv = f(); ++rv.a; return rv; }

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172647082

.

Reply to this email directly or view it on GitHub <

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172654197

.

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172658607

.

Reply to this email directly or view it on GitHub <

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172659947

.

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172661745

.

Reply to this email directly or view it on GitHub < https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172662459

.

Reply to this email directly or view it on GitHub< https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172664797

.

— Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172668150 .

andyprowl avatar Jan 18 '16 22:01 andyprowl

OK. There are two things there: (1) technical and design explanation for the ban (which predated C++); (2) your assertion that it makes things cleaner.

(2) isn't established as a truth yet.

Does that mean (1) would not satisfy you as "strong argument"?

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 4:25 PM (GMT-06:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

A strong argument would be one that explains why banning a feature that makes things cleaner (for the reasons I outlined my previous message) is considered "good". On Jan 18, 2016 11:14 PM, "Gabriel Dos Reis" [email protected] wrote:

What do you consider a strong argument?

The ask is so we can get to the chase.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 3:56 PM (GMT-06:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

It would make things cleaner because the user of a function would know from its declaration what the returned values/objects are, and what they mean: that's because they would have names.

With tuples and binding, those names would have to be redeclared at each point of call, which potentially opens consistency issues. It also forces to modify all points of call if the order of the elements in the tuple changes.

These points have been made already in this thread, so I'm not going to repeat them. Basically, my point is that while returning tuples with binding might be a useful idiom in certain situations (IIRC Nicol Bolas did provide a reasonable example), it is not always the cleanest solution. In fact, IMO it is not the cleanest solution in most cases, but that's of course subjective.

I would like to hear a strong argument against the in-place definition of the return type. On Jan 18, 2016 10:47 PM, "Gabriel Dos Reis" [email protected] wrote:

Why and how does it make things cleaner?

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:42 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Ok, I know I'm gonna sound overly polemic, which I don't want, but I really can't help it: "we are not going back", "we do not recommend it" and "the ban has always been there" are not counter-arguments.

I'm not saying there aren't counter arguments, mind you, but I would like to hear them. Why is "going back" (more like, "generalizing the current rules") not an option, if it makes things cleaner? On Jan 18, 2016 10:33 PM, "Gabriel Dos Reis" [email protected] wrote:

Introducing multiple toplevel "entities" in one declaration isn't something we recommend.

But this specific ban has been in C++ long before there was C++.

It does not look like we're going back. So we can "discuss" it, but it does not look like we are going back.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:28 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Gabi, would you mind commenting on why is that good? I understand you guys have a difficult task, but this kind of intervention isn't very useful for educational purposes.

I myself believe Michael's approach would make returning multiple values much cleaner than using tuples and binding in certain cases. No need to redeclare names at every call point etc. I made these observations already, I won't bother you further.

But I would like to understand why defining the return type "inline" would be a bad thing, because it isn't clear to me. On Jan 18, 2016 10:09 PM, "Gabriel Dos Reis" <[email protected]

wrote:

Was disallowed in C++, long before we had a Standard. Good!

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 12:36 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

IMO, struct { ... } foo(); would be the best except that it seems to be disallowed by the standards. Why? Perhaps so compilers would be forced to reject code where the trailing semicolon was forgotten for a struct/class declaration? (A web search for "c++ new types may not be defined in a return type" indicates that mistake seems to be common...)

struct { int a; foo b; } f() { return { 42, foo('x') }; }

decltype(f()) g() { auto rv = f(); ++rv.a; return rv; }

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172647082

.

Reply to this email directly or view it on GitHub <

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172654197

.

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172658607

.

Reply to this email directly or view it on GitHub <

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172659947

.

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172661745

.

Reply to this email directly or view it on GitHub < https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172662459

.

Reply to this email directly or view it on GitHub< https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172664797

.

Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172668150 .

Reply to this email directly or view it on GitHubhttps://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172670055.

gdr-at-ms avatar Jan 18 '16 22:01 gdr-at-ms

I won't go as far as calling it a "truth", but I think my arguments in favor of (2) are solid and seem to be shared by more users in this thread. What makes you think they are not?

An explanation of (1) (i.e. the reasons behind the design decision to ban it) would certainly help.

andyprowl avatar Jan 18 '16 22:01 andyprowl

Mainly because you then wouldn't need to invent a name for the type of the returned struct.

It'd also be more clear that the struct exists solely so that the function may return multiple values.

On Mon, Jan 18, 2016 at 4:46 PM, Gabriel Dos Reis [email protected] wrote:

Why and how does it make things cleaner?

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:42 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Ok, I know I'm gonna sound overly polemic, which I don't want, but I really can't help it: "we are not going back", "we do not recommend it" and "the ban has always been there" are not counter-arguments.

I'm not saying there aren't counter arguments, mind you, but I would like to hear them. Why is "going back" (more like, "generalizing the current rules") not an option, if it makes things cleaner? On Jan 18, 2016 10:33 PM, "Gabriel Dos Reis" [email protected] wrote:

Introducing multiple toplevel "entities" in one declaration isn't something we recommend.

But this specific ban has been in C++ long before there was C++.

It does not look like we're going back. So we can "discuss" it, but it does not look like we are going back.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:28 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Gabi, would you mind commenting on why is that good? I understand you guys have a difficult task, but this kind of intervention isn't very useful for educational purposes.

I myself believe Michael's approach would make returning multiple values much cleaner than using tuples and binding in certain cases. No need to redeclare names at every call point etc. I made these observations already, I won't bother you further.

But I would like to understand why defining the return type "inline" would be a bad thing, because it isn't clear to me. On Jan 18, 2016 10:09 PM, "Gabriel Dos Reis" [email protected] wrote:

Was disallowed in C++, long before we had a Standard. Good!

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 12:36 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

IMO, struct { ... } foo(); would be the best except that it seems to be disallowed by the standards. Why? Perhaps so compilers would be forced to reject code where the trailing semicolon was forgotten for a struct/class declaration? (A web search for "c++ new types may not be defined in a return type" indicates that mistake seems to be common...)

struct { int a; foo b; } f() { return { 42, foo('x') }; }

decltype(f()) g() { auto rv = f(); ++rv.a; return rv; }

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172647082

.

Reply to this email directly or view it on GitHub <

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172654197

.

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172658607

.

Reply to this email directly or view it on GitHub < https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172659947

.

Reply to this email directly or view it on GitHub< https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172661745

.

— Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172662459 .

MichaelCook avatar Jan 18 '16 22:01 MichaelCook

I have absolutely no doubt that you believe your arguments appear to you as solid - otherwise you wouldn't put it forward. As, a matter of fact, you aren't the first to make that suggestion (and you wouldn't be the last). But as you said "you wouldn't go as far as calling it a 'truth'". So we are reduced as far as (2) goes to opinion. And I am not surprised. I am glad we are agreeing on that at least.

Regarding (1), what do think the effect on the C++ type system should be? Can such declarations be non-defining? If yes, how do you ensure coherence across translation units? If no, why? Wouldn't that be a severe limitation? Can they be templates? Does that make the return type template? If not why? Can such return type have member templates? Can they have friends? How is name lookup supposed to work? Where do the member names live? These are just starting questions -- I am sure they are more that would need to be resolved.

It would be helpful to have answers to those questions, so some of us can assess how that might make things cleaner.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 4:36 PM (GMT-06:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

I won't go as far as calling it a "truth", but I think my arguments in favor of (2) are solid and seem to be shared by more users in this thread. What makes you think they are not?

An explanation of (1) (i.e. the reasons behind the design decision to ban it) would certainly help.

Reply to this email directly or view it on GitHubhttps://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172672372.

gdr-at-ms avatar Jan 18 '16 22:01 gdr-at-ms

But how does it work concretely? I get the economy of not naming a tuple -- and for that, I wouls contend that tuple works just fine. But, once you make the economy of not naming the type, you get a host of other problems -- see my reply to Andy. There is no free lunch, something has to give. It is not clear that the economy of naming the type outweighs the complications we get in return. Hence, my probing to understand why people believed it majes things cleaner.

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 4:50 PM (GMT-06:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Mainly because you then wouldn't need to invent a name for the type of the returned struct.

It'd also be more clear that the struct exists solely so that the function may return multiple values.

On Mon, Jan 18, 2016 at 4:46 PM, Gabriel Dos Reis [email protected] wrote:

Why and how does it make things cleaner?

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:42 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Ok, I know I'm gonna sound overly polemic, which I don't want, but I really can't help it: "we are not going back", "we do not recommend it" and "the ban has always been there" are not counter-arguments.

I'm not saying there aren't counter arguments, mind you, but I would like to hear them. Why is "going back" (more like, "generalizing the current rules") not an option, if it makes things cleaner? On Jan 18, 2016 10:33 PM, "Gabriel Dos Reis" [email protected] wrote:

Introducing multiple toplevel "entities" in one declaration isn't something we recommend.

But this specific ban has been in C++ long before there was C++.

It does not look like we're going back. So we can "discuss" it, but it does not look like we are going back.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 1:28 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

Gabi, would you mind commenting on why is that good? I understand you guys have a difficult task, but this kind of intervention isn't very useful for educational purposes.

I myself believe Michael's approach would make returning multiple values much cleaner than using tuples and binding in certain cases. No need to redeclare names at every call point etc. I made these observations already, I won't bother you further.

But I would like to understand why defining the return type "inline" would be a bad thing, because it isn't clear to me. On Jan 18, 2016 10:09 PM, "Gabriel Dos Reis" [email protected] wrote:

Was disallowed in C++, long before we had a Standard. Good!

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 12:36 PM (GMT-08:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

IMO, struct { ... } foo(); would be the best except that it seems to be disallowed by the standards. Why? Perhaps so compilers would be forced to reject code where the trailing semicolon was forgotten for a struct/class declaration? (A web search for "c++ new types may not be defined in a return type" indicates that mistake seems to be common...)

struct { int a; foo b; } f() { return { 42, foo('x') }; }

decltype(f()) g() { auto rv = f(); ++rv.a; return rv; }

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172647082

.

Reply to this email directly or view it on GitHub <

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172654197

.

Reply to this email directly or view it on GitHub<

https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172658607

.

Reply to this email directly or view it on GitHub < https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172659947

.

Reply to this email directly or view it on GitHub< https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172661745

.

Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172662459 .

Reply to this email directly or view it on GitHubhttps://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172674668.

gdr-at-ms avatar Jan 18 '16 22:01 gdr-at-ms

I would say that struct { ... } f(); should be merely shorthand for struct t { ... }; t f(); Exactly the same with all the same rules, limitations, etc., with the only difference being that in the former the return type is anonymous.

On Mon, Jan 18, 2016 at 5:54 PM, Gabriel Dos Reis [email protected] wrote:

I have absolutely no doubt that you believe your arguments appear to you as solid - otherwise you wouldn't put it forward. As, a matter of fact, you aren't the first to make that suggestion (and you wouldn't be the last). But as you said "you wouldn't go as far as calling it a 'truth'". So we are reduced as far as (2) goes to opinion. And I am not surprised. I am glad we are agreeing on that at least.

Regarding (1), what do think the effect on the C++ type system should be? Can such declarations be non-defining? If yes, how do you ensure coherence across translation units? If no, why? Wouldn't that be a severe limitation? Can they be templates? Does that make the return type template? If not why? Can such return type have member templates? Can they have friends? How is name lookup supposed to work? Where do the member names live? These are just starting questions -- I am sure they are more that would need to be resolved.

It would be helpful to have answers to those questions, so some of us can assess how that might make things cleaner.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 4:36 PM (GMT-06:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

I won't go as far as calling it a "truth", but I think my arguments in favor of (2) are solid and seem to be shared by more users in this thread. What makes you think they are not?

An explanation of (1) (i.e. the reasons behind the design decision to ban it) would certainly help.

Reply to this email directly or view it on GitHub< https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172672372

.

— Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172675374 .

MichaelCook avatar Jan 18 '16 22:01 MichaelCook

I'd like to take a crack at this because I don't think its that difficult.

What we are (at least I am) suggesting is essentially this:

struct func_return_type
{
    int var;
};

func_return_type func();

But with the convenience of writing it like this:

struct
{
    int var;
}
func();

Templates would logically be copied from the function and duplicated to the struct so:

template<typename T>
struct
{
    T var;
}
func(const T& t);

would be the same as:

template<typename T>
struct func_return_type
{
    T var;
};

template<typename T>
func_return_type<T> func(const T& t);

So as to the preliminary questions:

Q) Can such declarations be non-defining?

Yes.

Q) If yes, how do you ensure coherence across translation units?

The same way you ensure any return type coherence across translation units.

Q) Wouldn't that be a severe limitation?

No, we don't have a problem with incoherent built in return types so I don't see why it would be a problem for aggregate return types.

Q) Can they be templates?

The function can and therefore so can its return type (they are templated with the functions template parameters)

Q) Does that make the return type template?

If it works for simple return types why not for aggregate?

Q) Can such return type have member templates?

I don't see why not, this is, after all, short hand notation for what we can already achieve using a distinct struct. All rules should be identical except the convenience of not having to give the return struct a name and its taking its template parameters from the function.

Q) Can they have friends?

No. How can anonymous structs have friends?

Q) How is name lookup supposed to work?

There is no name to look up.

Q) Where do the member names live?

A machine generated typename prefixed by the function signatuse rather like lambda types.

galik avatar Jan 18 '16 23:01 galik

@gdr-at-ms

Hence, my probing to understand why people believed it majes things cleaner.

The "cleaner" is on the usage side of things where you don't end up with a bunch of variables littering the scope after the multiple return value has been dealt with. Please review my previous posts as to why I believe struct is much cleaner in use than the current tuple proposal and even the various 'fixes' that have been suggested.

galik avatar Jan 18 '16 23:01 galik

Where is 't' coming from? When are two 't' different or equal? Where do the names members of 't' go? What do you mean by exactly the same rules, limitations? Exactly like what else?

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 5:00 PM (GMT-06:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

I would say that struct { ... } f(); should be merely shorthand for struct t { ... }; t f(); Exactly the same with all the same rules, limitations, etc., with the only difference being that in the former the return type is anonymous.

On Mon, Jan 18, 2016 at 5:54 PM, Gabriel Dos Reis [email protected] wrote:

I have absolutely no doubt that you believe your arguments appear to you as solid - otherwise you wouldn't put it forward. As, a matter of fact, you aren't the first to make that suggestion (and you wouldn't be the last). But as you said "you wouldn't go as far as calling it a 'truth'". So we are reduced as far as (2) goes to opinion. And I am not surprised. I am glad we are agreeing on that at least.

Regarding (1), what do think the effect on the C++ type system should be? Can such declarations be non-defining? If yes, how do you ensure coherence across translation units? If no, why? Wouldn't that be a severe limitation? Can they be templates? Does that make the return type template? If not why? Can such return type have member templates? Can they have friends? How is name lookup supposed to work? Where do the member names live? These are just starting questions -- I am sure they are more that would need to be resolved.

It would be helpful to have answers to those questions, so some of us can assess how that might make things cleaner.

-------- Original message -------- From: Andy Prowl [email protected] Date: 1/18/2016 4:36 PM (GMT-06:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

I won't go as far as calling it a "truth", but I think my arguments in favor of (2) are solid and seem to be shared by more users in this thread. What makes you think they are not?

An explanation of (1) (i.e. the reasons behind the design decision to ban it) would certainly help.

Reply to this email directly or view it on GitHub< https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172672372

.

Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172675374 .

Reply to this email directly or view it on GitHubhttps://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172676096.

gdr-at-ms avatar Jan 18 '16 23:01 gdr-at-ms

I read it. Actually, I have been quietly following the discussion until now.

Unfortunately, I am not convinced by the anonymous struct. Yet. I like the named struct too. I would like to be persuaded, if people could look at this in the global context of day-to-day programming. It is not just the usage that matters. Definitions matter too a lot, so does the overall effect of new language rules. For example, introducing a new set of name lookup rules, just for the benefit of omitting a type name does not persuade as "cleaner".

There is a lot more to this than a terse syntax.

-------- Original message -------- From: Galik [email protected] Date: 1/18/2016 5:17 PM (GMT-06:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

@gdr-at-mshttps://github.com/gdr-at-ms

Hence, my probing to understand why people believed it majes things cleaner.

The "cleaner" is on the usage side of things where you don't end up with a bunch of variables littering the scope after the multiple return value has been dealt with. Please review my previous posts as to why I believe struct is much cleaner in use that the current tuple proposal and even the various 'fixes' that have been suggested.

Reply to this email directly or view it on GitHubhttps://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172678631.

gdr-at-ms avatar Jan 18 '16 23:01 gdr-at-ms

On Mon, Jan 18, 2016 at 6:18 PM, Gabriel Dos Reis [email protected] wrote:

Where is 't' coming from?

It's just a made up name for this example.

When are two 't' different or equal?

Where do the names members of 't' go? What do you mean by exactly the same rules, limitations? Exactly like what else?

Nothing new is being proposed for comparison of types or instances of those types. The only thing that'd be different is whether the type of the struct returned from the function has a name or not.

MichaelCook avatar Jan 18 '16 23:01 MichaelCook

Yes, something new is being proposed!

The closest we have in existing language are anonymous unions. They are always distinct.

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 5:28 PM (GMT-06:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

On Mon, Jan 18, 2016 at 6:18 PM, Gabriel Dos Reis [email protected] wrote:

Where is 't' coming from?

It's just a made up name for this example.

When are two 't' different or equal?

Where do the names members of 't' go? What do you mean by exactly the same rules, limitations? Exactly like what else?

Nothing new is being proposed for comparison of types or instances of those types. The only thing that'd be different is whether the type of the struct returned from the function has a name or not.

Reply to this email directly or view it on GitHubhttps://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172680570.

gdr-at-ms avatar Jan 18 '16 23:01 gdr-at-ms

@gdr-at-ms

The closest we have in existing language are anonymous unions.

What about anonymous structs used to form typedef's?

typedef struct { /* stuff*/ } my_typename;

galik avatar Jan 18 '16 23:01 galik

And by the way, members of anonymous unions get promoted to the enclosing named or global scope. So, you are proposing something new.

There is nothing wrong with proposing something new. But we have to acknowledge that and work out the details, and then see whether they are as simple and cleaner as we originally thought.

-------- Original message -------- From: Michael Cook [email protected] Date: 1/18/2016 5:28 PM (GMT-06:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

On Mon, Jan 18, 2016 at 6:18 PM, Gabriel Dos Reis [email protected] wrote:

Where is 't' coming from?

It's just a made up name for this example.

When are two 't' different or equal?

Where do the names members of 't' go? What do you mean by exactly the same rules, limitations? Exactly like what else?

Nothing new is being proposed for comparison of types or instances of those types. The only thing that'd be different is whether the type of the struct returned from the function has a name or not.

Reply to this email directly or view it on GitHubhttps://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172680570.

gdr-at-ms avatar Jan 18 '16 23:01 gdr-at-ms

They are given a name by the programmer -- that is why that restriction is there. That name is called "class-name for linkage purposes". It is no ordinary typedef name. It also serves to name the members. It serves for checking coherence across translation units.

-------- Original message -------- From: Galik [email protected] Date: 1/18/2016 5:36 PM (GMT-06:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

@gdr-at-mshttps://github.com/gdr-at-ms

The closest we have in existing language are anonymous unions.

What about anonymous struct's used to form typedef's?

typedef struct { /* stuff*/ } my_typename;

Reply to this email directly or view it on GitHubhttps://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172681890.

gdr-at-ms avatar Jan 18 '16 23:01 gdr-at-ms

@gdr-at-ms I think maybe lambda functions may have something in common too, being that they are sort of anonymous structs. I don't think anyone requires these anonymous structs to be a general feature so that anyone can define an anonymous struct anywhere. Why would we want that? It is simply a mechanism to allow a function to return a struct without having to typename it and without needing to typename it because, like a lambda it can be bound to an auto.

galik avatar Jan 18 '16 23:01 galik

@gdr-at-ms wrote:

Yes, something new is being proposed!

I don't see why there's any novelty, so perhaps I'm misunderstanding?

All we'd need is to give the struct a name that doesn't depend on the TU -- isn't that sufficient? For example, for a function function_name that returns a struct, it'd be easy to specify the struct's name as prepending _R (e.g., _Rfunction_name) or appending __ret (e.g., function_name__ret) or similar -- those names are already reserved to the implementation and can't conflict with user identifiers. That would work for both member and nonmember functions.

So then

struct{ int x, y } function_name();

or

auto function_name() -> struct{ int x, y; };

could be defined as exactly equivalent to (and just syntactic sugar for)

struct{ int x, y } _Rfunction_name;
_Rfunction_name function_name();

and I don't think there are any issues with ODR or declarations or templates or the other things you mentioned.

There may be (other) valid arguments against the idea/technique, but I don't think language novelty/complexity is one. This would be straightforward to specify -- is there a reason I've overlooked why this wouldn't work?

hsutter avatar Jan 19 '16 00:01 hsutter

Lambda would come close, but see they have district name all the times, even if they look the same token-wise. So that is not like the rules proposed earlier. Furthermore, you can't access captured members of lambdas as struct members - see related core issues. As a matter of fact, if you could then you would have an isomorphic way of constructing anonymous structs on the way - except you would always get distinct structs even if the tokens are the same.

-------- Original message -------- From: Galik [email protected] Date: 1/18/2016 6:41 PM (GMT-05:00) To: isocpp/CppCoreGuidelines [email protected] Cc: Gabriel Dos Reis [email protected] Subject: Re: [CppCoreGuidelines] F.41 should suggest returning a struct, not a tuple (#364)

@gdr-at-mshttps://github.com/gdr-at-ms I think maybe lambda functions may have something in common to being that they are sort of anonymous structs. I don't think anyone requires these anonymous structs to be a general feature so that anyone can define an anonymous struct anywhere. Why would we want that? It is simple a mechanism to allow a function to return a struct without having to name it and without needing to name it because, like a lambda it can be bound to an auto.

Reply to this email directly or view it on GitHubhttps://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172683009.

gdr-at-ms avatar Jan 19 '16 01:01 gdr-at-ms

FYI: Many of the issues regarding unnamed structs as return values have been actively discussed on the std-proposals mailing list. That discussion started out talking about "tagged-tuples", but then it morphed into "why are we doing complex stuff like tagged_tuple when we can just return an unnamed struct?" So the topic shifted.

The general consensus on the thread (among those who wanted the feature) was:

  1. Unnamed structs are the same for the same function declaration.

  2. Unnamed structs are different for different function declarations, even if they define the same types and names in the same order. They are also incompatible with one another, just as if you had declared two identical types with the same names.

  3. Rule 2 includes template instantiations. So each instantiation returns its own separate type.

  4. In order to put a declaration in a header and a definition in a source file, you either need for item 1 to be relaxed if the overload is the same as a previously declared one, or new syntax needs to be added to allow auto-deduction of a function declaration/definition's return type as the return type of the same function if it was previously declared. So this would work:

    struct {int f} func(params);
    auto func(params){...} //Always deduces as `decltype(func(params))`, if it was previously declared.
    

    This latter feature was the subject of a separate thread, since it has utility beyond unnamed struct return values.

NicolBolas avatar Jan 19 '16 01:01 NicolBolas

@gdr-at-ms So this is the first time I am seeing a technical difficulty. You are right, what to do with two identical structs from different functions?

@hsutter Your suggestion of having a distinct name based on the function name works and is sort of what I had in mind. However it would mean that some things you might expect to work will not.

For example if we have a very common return type so we want to reuse the variable that accepts the return value:

auto rv = func1(); // returns {bool, int}

// oops can't reuse rv even though struct is same layout
rv = func2(); // returns {bool, int}

I suppose there is always:

rv = static_cast<decltype(rv)>(func2()); // horrible and error prone

To satisfy that case one could either fall back on defining a proper named struct or perhaps use some form of name mangling to generate the struct's typename based on its signature (aka its variable types and their order)?

galik avatar Jan 19 '16 01:01 galik

@galik wrote:

what to do with two identical structs from different functions?

shrug They should be different structs. The struct is the function's return type; it should be derived from the function name. That's what we'd do today by hand.

For example if we have a very common return type so we wan to reuse the variable that accepts the return value:

If we have a common type we should name it, and we can already do that today.

The use case we are talking about is simply "this function returns multiple values that don't deserve their own reusable name, we'd like to group them." If that combination of variables is common, of course it should be named which we can already do today. I thought the case we were interested in is when it's not -- when it's a one-off "here are the possibly-unrelated outputs this function yields" and we don't want to pollute the namespace. Then we could just generate a name.

hsutter avatar Jan 19 '16 02:01 hsutter

@hsutter I am absolutely fine with what you are proposing, I love the simplicity of it being the syntactic sugar for what we would do right now.

I am over thinking this but mainly because I am finding the various possibilities interesting to explore. In fact I just wrote a whole piece about this but I realised I had moved out of the territory of the humble struct and, as such it is not really relevant here.

You are correct in that two differently defined structs are different types and should be different types. Making them the same type is fundamentally different from what being a struct is all about.

galik avatar Jan 19 '16 02:01 galik

@NicolBolas I wish I could say I am surprised.

Based on my experience looking at this over the years, I suspect that any solution to this will be a novelty, no matter how it is cut o dieced. And it is going to surprise some people. There is nothing exactly like this today in the language -- the type system has been carefully designed to be nominal, hence anonymous classes being different, etc.

I asked earlier if such structs would have member templates just like any other structs, and I was told "yes". Well, I would like to see the details work out :-)

It is possible, as it was claimed that, this is overthinking but we need a real detailed spec -- not just a sketch. But I seriously doubt it is overthinking -- I would like to be proven wrong.

Because of redeclarations, and because of how function templates and overloading work (e.g. any two function templates that differ only in return type are overload), I suspect any solution is going to surprise someone because it is going to depart from known rules today.

gdr-at-ms avatar Jan 19 '16 02:01 gdr-at-ms

@hsutter

All we'd need is to give the struct a name that doesn't depend on the TU

That in itself is a novetly. Even lambdas don't behave that way -- yet they are the closest you can get to this issue. You asked whether that would be sufficient. I doubt it.

There is too much focus on the implementation details, in the small, and not enough global view on how it is to behave. Where do the member names go? They have to live somewhere. What new lookup rules do programmers need to learn?

gdr-at-ms avatar Jan 19 '16 02:01 gdr-at-ms

While we are focusing on modifying the struct in this fascinating discussion on possible future coding practice the thread's original purpose has been a little subverted (probably by me).

Regardless of language support for making the struct appear more built-in as a multiple return type, it would still be good if those involved in formulating these guidelines would at least think about reviewing the advice for what we should practice right now.

I feel those of us supporting the use of a (named) struct have made a good case for why we believe it might be recommended over the tuple & tie. There are proposals that are in the pipeline that promise to make using the tuple less problematic but any of those must be years away from being a reality.

galik avatar Jan 19 '16 04:01 galik

I find Herb's approach reasonable, simple, and effective. I also wouldn't want identical return types for different functions to be assignable to each other. They are different types with different semantics. If they are not, then we should just separately define a named struct and use it as the return type of all those functions - like we would today.

Also, Galik is correct - I started this thread to object on the formulation of a guideline, not to discuss how to improve the language. Since then, the guideline has been changed to recommend tuples or structs. I am still not sure I would recommend tuples (although, admittedly, Nicol Bolas's arguments are valid), but I see this as an improvement over the original draft.

Anyway I don't mind discussing potential improvements to the language, and this looks like a useful generalization to me. On Jan 19, 2016 05:11, "Galik" [email protected] wrote:

While we are focusing on modifying the struct in this fascinating discussion on possible future coding practice the thread's original purpose has been a little subverted (probably by me).

Regardless of language support for making the struct appear more built-in as a multiple return type, it would still be good if those involved in formulating these guidelines would at least think about reviewing the advice for what we should practice right now.

I feel those of us supporting the use of a (named) struct have made a good case for why we believe it might be recommended over the tuple & tie. There are proposals that are in the pipeline that promise to make using the tuple less problematic but any of those must be years away from being a reality.

— Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172732138 .

andyprowl avatar Jan 19 '16 10:01 andyprowl

Yeah, @galik is true about the thread being for guidelines for current code, and the question of the benefits of a struct against a tuple , and vice versa. I'm not sure the benefit takes over the inconvenience of a tuple in its today form, in regards of the arguments written here.

That said, I would like to see what a "anonymous return struct" would look like in some years, even if, pragmatically, this thing can already be done today (even with dreadful #define as ornament, if really needed).

Pazns avatar Jan 19 '16 10:01 Pazns

@gdr-at-ms

There is too much focus on the implementation details, in the small, and not enough global view on how it is to behave. Where do the member names go? They have to live somewhere. What new lookup rules do programmers need to learn?

I agree the question should stop being the mechanics of a possible language extension. (However, in my suggestion that it be sugar to omit manually writing the struct's name, these and all the other questions earlier in the thread have simple answers: Same as a named struct. We'd just generate the name; nothing else new/weird/mysterious/subtle.)

The truly global-view question is: "Which leads to better (clearer, less error-prone, etc.) calling code now and under anticipated future C++ evolution -- (a) returning a struct with named members that you access with .name, or (b) returning a tuple with unnamed members you access with get<N>? What are are the advantages and drawbacks of each, and is one clearly preferable?"

  • If (a) is clearly better, we can make it easier to do so by removing a longstanding (intentional) language limitation in a simple way with no new or surprising semantics.
  • If (b) is clearly better, we should just stick with "use tuples, don't return structs."
  • If each has an advantage the other does not, we should recommend both as valid alternatives.

So we should get back to that question, which applies to code we can already write without any language extension. (As a side effect, the answer would incidentally happen to help inform whether to consider a trivial language extension.)

The place to start would be a summary list of advantages/drawbacks of each alternative. To restart that, some advantages of (a) are that:

  • A member variable name is more descriptive and intent-documenting than a general type name. For example, auto r = f(); use(r.num_colors); is arguably clearer code than auto r = f(); use(get<int>(r));, Definitely worse is auto r = f(); use(get<1>(r));, which you must use if there are two entries of the same type and so you have to resort to using indexes (and incidentally this violates guidelines about magic numbers).
  • It eliminates classes of errors such as forgetting which of the two ints in the tuple have which meaning. For example, it's easier to write and code-review auto r = f(); colors = r.num_colors; shapes = r.num_shapes; than auto r = f(); colors = get<0>(r); shapes = get<1>(r).
  • A caller who wants to use named variables has them always initialized (without waiting for the structured bindings proposal). For example, auto r = f(); use( r.num_colors, r.num_shapes ); has no uninitialized variables, whereas int num_colors, num_shapes; std::tie(num_colors, num_shapes) = f(); use( num_colors, num_shapes ); does (even if you zero-initialize, it's not a real initialization; thinking "just jamming some initial value in there is real initialization" is a popular misconception).

On the other hand, an advantage of (b) is:

  • Symmetry with parameter lists, because parameter names are not required on declarations and at the call site parameters are effectively unnamed (they have formal names but the caller doesn't use those and it's easy to mix up the order of parameters of the same type etc.) and so returning a tuple preserves symmetry (e.g, auto f(int, string, foo) -> tuple<float, bar>; is arguably more symmetric than struct f_ret { float specific_gravity; bar employee_id; }; f_ret f(int, string, foo); which adds names for only one group).

So there's a start... any more?

hsutter avatar Jan 19 '16 15:01 hsutter

@hsutter :

On the other hand, an advantage of (b) is: [...]

Is this really an advantage ? An unnamed parameter in a function declaration seems a bit error-prone in the first place.

Pazns avatar Jan 19 '16 16:01 Pazns

Advantages of a)

  • Doesn't leave a bunch of temporary variable names littering the scope after the function return value has been dealt with. Using std::tie() does.
  • Allows simple enhancements such as adding operator bool() const; to facilitate this construct:
if(auto rv = func())
{
    // don't pollute general scope with the return value at all
}

// nothing lives on here
  • Can be templated
  • Efficiency of RVO

Disadvantage b)

  • Possible to initialize the wrong variable using std::tie()

galik avatar Jan 19 '16 16:01 galik

IMHO, the ideal syntax for MRV is what Python and Go do:

v1, v2 = some_func(p1, p2, p3)

This is nice and clean. C++ can't currently do this as cleanly. Instead, you have to do:

type v1; type v2; std::tie(v1, v2) = some_func(p1, p2, p3); // assuming some_func returns a tuple or pair

However, this is much closer to what I consider the ideal than something like:

struct return_for_some_func { type v1; type v2; }

return_for_some_func some_func(p1, p2, p3);

The primary advantages for std::tie/std::tuple in my mind are:

  • Reduced verbosity (in the prototype and in the not having to declare a struct)
  • Closer to what the "ideal" (IMHO) syntax would be for MRV if it were ever directly supported.

While it is true that you can bind the wrong parameter in some cases, I respectfully submit that that falls in the category of "it hurts when I do this- so don't do it." It's not any more dangerous or likely than passing a parameter in the wrong slot. It can be dealt with in the same way too: use strong types.

I agree that the extra declarations are annoying. It would be nice if C++ had an := operator like Go, but that's a dream unlikely to come true before the heat death of the universe.

I would continue to suggest that the guideline is use std::tuple/std::pair and std::tie, rather than preferring a struct for one-off MRV. The additional type declarations are preferable to the additional struct declarations and the need to fabricate increasingly silly type names for uniqueness.

For cases where you frequently use the same values together, a struct begins to make a lot more sense.

On Tue, Jan 19, 2016 at 11:22 AM Galik [email protected] wrote:

Advantages of a)

Doesn't leave a bunch of temporary variable names littering the scope after the function return value has been dealt with. Using std::tie() does.

Allows simple enhancements such as adding operator bool() const; to facilitate this construct:

if(auto rv = func()) { // don't pollute general scope with the return value at all } // nothing lives on here

Can be templated

Efficiency of RVO

Disadvantage b)

  • Possible to initialize the wrong variable using std::tie()

— Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-172905347 .

nadiasvertex avatar Jan 19 '16 19:01 nadiasvertex

@galik @nadiasvertex My opinions about std::tie are well known. Hint: I am an entusiastic supporter of the simple version of structutured binding, which I think resolves (largely) the call for Python-style multiple value binding.

@hsutter @galik I think there are valid cases where using named members at the definition site can bring some clarity to the code. But, I posit that in general there are more use sites than call sites. Which is why I personally believe that we should re-evaluate the urgency of an 'unnamed struct in return type' after we have structured binding implemented and in widespread use. A syntactic sugar should be a sugar and not bring new type system issues. But let's discuss that another day.

I conjecture the complains of get<N> will be overblown in the presence of structured binding. When those functions exist, they will mostly be invoked implicitly by the compiler to give you named multiple return values -- so you will not be writing them as often as you thought, if ever.

That brings me to state this for the current guidelines for current codes: I think the guidelines could be amended to state a preference for returning a tuple, but note that in many cases, using a type with named members might bring some clarity at the definition site.

gdr-at-ms avatar Jan 19 '16 19:01 gdr-at-ms

@gdr-at-ms: 100% agreement with your last note. Thanks.

hsutter avatar Jan 19 '16 19:01 hsutter

@nadiasvertex :

  • Reduced verbosity (in the prototype and in the not having to declare a struct)

Verbosity is also Clarity. Especially in collaborative programming, it is always good to have a minimum of explicitness in an algorithm. Even bad programmers who never write comments will be forced to write C++ lines comprehensible by other human beings. Also, arbitrary unnamed order seems to look like a bad case of Magic Number Pox. Regrettable order, similar type semantics, nice unreadable templated error messages, and your computer will begin to smell like Hell.

C++ should stay readable and usable by newcomers at least in a minimal way, and altering the way values are returned is a big thing ; does recommending tie and tuple, over (a bit more) verbose ways, really help this ?

Pazns avatar Jan 20 '16 01:01 Pazns

@pazns Verbosity != clarity. As a concrete example, consider any legal document. Or the C++ standard itself. Or Java. An abundance of syntax is as distracting as a paucity. I tend to feel the balance lies in a different place than you. But that's okay. ☺

On Tue, Jan 19, 2016, 8:39 PM Pazns [email protected] wrote:

@nadiasvertex https://github.com/nadiasvertex :

  • Reduced verbosity (in the prototype and in the not having to declare a struct)

Verbosity is also Clarity. Especially in collaborative programming, it is always good to have a minimum of explicitness in an algorithm. Even bad programmers who never write comments will be forced to write C++ lines comprehensible by other human beings. Also, arbitrary unnamed order seems to look like a bad case of Magic Number Pox. Regrettable order, similar type semantics, nice unreadable templated error messages, and your computer will begin to smell like Hell.

C++ should stay readable and usable by newcomers at least in a minimal way, and altering the way values are returned is a big thing ; does recommending tie and tuple, over (a bit more) verbose ways, really help this ?

— Reply to this email directly or view it on GitHub https://github.com/isocpp/CppCoreGuidelines/issues/364#issuecomment-173055256 .

nadiasvertex avatar Jan 20 '16 02:01 nadiasvertex

@nadiasvertex : With a program, as well as a law, you should be able to read the maximum of informations, in a minimum of time, but also with the least possible of ambiguity. We are writing C++, not Assembly. A bunch of extra letters in your instructions is not going to hurt you. I agree right now it is cumbersome, because a returned struct should be unique as well as related to its parent method, so big names like "doSomething_r" ahead, then an update of the language could be really helpful one day. Even if I have difficulties to see how it can be done. How one can allocate room before a call for the returned struct if she is anonymous ?

Still, I can't see why a composite return value without struct encapsulation would be easier and faster to handle, when its concern is primarily to show less letters on the screen than to care about programming logic and understandability.

Pazns avatar Jan 20 '16 10:01 Pazns

Hi,

I stumble upon this thread a long time after it was active, but I find the discussions in here really interesting.

I have another possible way to express multiple return values, using idiomatic C++98.

#define multiple_return struct

multiple_return my_function  
{
  // outputs
  int val1, val2;

  my_function(int param1, bool param2)
  {
      //val1 = ...
      //val2 = ...
  }
};

int main()
{
  auto foo = my_function(1, true);
  std::cout << foo.val1;
}

The philosophy behind this code is that it is better to devise a good function name, than to try and name correctly the return type.

This code includes a preprocessor definition, but it is just in order to improve readability (and it could be avoided). However, it might help in order to refrain future maintainers from turning the return type into a full fledged class.

The drawback is that you have to write the function name twice.

Anyway, I would very much like to see a way to express named multiple return value in C++ (in my opinion, using tuples as a return value may often lead to obscure code).

pthom avatar Aug 22 '17 13:08 pthom

@pthom The macro obscures your suggestion but I find what you are saying interesting. It is like the ultimate object oriented way of looking at the problem. Rather than seeing functions that return data we see objects that calculate themselves. You literally define the return type in terms of the operations needed to generate it.

Definitely food for thought.

galik avatar Aug 22 '17 13:08 galik

@pthom This could imply defining all the functions that return a specific type to be defined inside this type. This doesn't scale :(

viboes avatar Aug 22 '17 20:08 viboes

The drawback of structs is that you have to invent a name for them, hence the suggestions for anonymous struct.

However, in the mean time, since _naming is hard, why not propose idiomatic names in the guidelines?

For example, a function with multiple named ouputs and multiple named inputs can be achieved by using a namespace: just create a namespace whose name is the name of the function, and inside this namespace, propose some idiomatic inner names.

For example:

  • "in" and "out"for the input / output parameters
  • "_" for the function

So that a call to our function looks like

 auto r = my_function::_({.x = 2, .y = 3});
 cout << "Sum = " << r.Sum << " Mult = " << r.Mult << "\n";

And the function would look like:

namespace my_function
{
    // input parameters
    struct in {
        int x , y = 2;
        int k = 1; // inputs can have default values
    };
    // output results
    struct out {
      int Sum, Mult, Mult2;
    };

    out _(const in & in)
    {
        return { 
            .Sum = in.x + in.y, 
            .Mult = in.x * in.y,
            .Mult2 = (in.x + in.y) * in.k
        };
    }
}

I expect some more thumbs down because this leads to confusion between "functions" and "namespaces". However, this is what it boils down to: how to idiomatically associate a namespace to a function, with no metaprogamming and no addition to the language.

See https://godbolt.org/z/hjtuWK for a working example.

pthom avatar Oct 21 '19 13:10 pthom

C++17 sets some precedent here. The result types of std::to_chars and std::from_chars are to_chars_result and from_chars_result respectively.

jwakely avatar Oct 21 '19 14:10 jwakely

@pthom

The principle issue with proposing an idiom is that good idioms tend to be developed by consensus. Multiple people see a problem, come up with various solutions, and the failings of those solutions get whittled down over time until there are only a few clearly good ones, from which we then pick one.

We don't really have that yet. And your suggestion doesn't come from such a process. It's entirely novel, with at best limited real-world experience. That doesn't make for a good idiom.

Also, your suggestion flat-out doesn't work for member functions or overloaded operators, both of which may need to return multiple values. It also breaks ADL, since the function isn't in the same namespace as one of its parameters (though inline namespace can help). Of course, using a struct for input values kills ADL regardless.

jwakely's _result suggestion is at least based on something that is already being used, even if it is somewhat novel, only being utilized in one place in the standard. Indeed, giving the struct a name like that, even if it's one that's long, is probably the better solution compared to an anonymous struct language change. By giving it a real C++ typename, you solve a bunch of problems with anonymous-inline structs:

  • The function declaration is much more digestible by humans. There isn't this big, giant struct {bunch of stuff} sitting in front of the function name, so such declarations are much more readable.
  • It probably works better with any eventual reflection system, since it has a name.
  • Multiple overloads that use the same set of return values return the same type (as in to/from_chars), whereas the anonymous struct versions would give them different names.
  • If it turns out that multiple return values actually did represent a meaningful aggregation to which a name could be applied, it's much easier to turn one name into a more meaningful name than to turn an anonymous struct declaration into a meaningful name.

NicolBolas avatar Oct 21 '19 14:10 NicolBolas