returning void from constructor

148 views
Skip to first unread message

joerg....@pdv-fs.de

unread,
Jun 26, 2013, 8:10:46 AM6/26/13
to std-dis...@isocpp.org
Hi,

Consider this program:

    void func();
    struct A
    {
        A()
        {
          return func();
        }
    };

Is this allowed or not? Or more importantly: should this be allowed?
My unbiased vote would be yes. ;)

See also:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57724

Vlad from Moscow

unread,
Jun 26, 2013, 8:24:02 AM6/26/13
to std-dis...@isocpp.org, joerg....@pdv-fs.de
 
It looks like that paragraph #3 of section 6.6.3 The return statement should be updated the following way
 
3 A return statement with an expression of type void can be used only in functions with a return type of cv void or in constructors and destructors; the expression is evaluated just before the function returns to its caller.

среда, 26 июня 2013 г., 16:10:46 UTC+4 пользователь joerg....@pdv-fs.de написал:

Daniel Krügler

unread,
Jun 26, 2013, 8:34:02 AM6/26/13
to std-dis...@isocpp.org, joerg....@pdv-fs.de
2013/6/26 Vlad from Moscow <vlad....@mail.ru>:
>
> It looks like that paragraph #3 of section 6.6.3 The return statement should
> be updated the following way
>
> 3 A return statement with an expression of type void can be used only in
> functions with a return type of cv void or in constructors and destructors;
> the expression is evaluated just before the function returns to its caller.

This is necessary, but not sufficient. You also need to touch 12.1 p8

"A return statement in the body of a constructor shall not specify a
<ins>non-void</in> return value."

Interesting point is that we currently have no such wording for
destructors (at least I couldn't find it in 12.4).

- Daniel

> среда, 26 июня 2013 г., 16:10:46 UTC+4 пользователь joerg....@pdv-fs.de
> написал:
>>
>> Hi,
>>
>> Consider this program:
>>
>> void func();
>> struct A
>> {
>> A()
>> {
>> return func();
>> }
>> };
>>
>>
>> Is this allowed or not? Or more importantly: should this be allowed?
>> My unbiased vote would be yes. ;)
>>
>> See also:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57724
>>
> --
>
> ---
> You received this message because you are subscribed to the Google Groups
> "ISO C++ Standard - Discussion" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to std-discussio...@isocpp.org.
> To post to this group, send email to std-dis...@isocpp.org.
> Visit this group at
> http://groups.google.com/a/isocpp.org/group/std-discussion/.
>
>

Ville Voutilainen

unread,
Jun 26, 2013, 8:43:55 AM6/26/13
to std-dis...@isocpp.org
Why should it be allowed? Is this for some code generation solution or some such?

Vlad from Moscow

unread,
Jun 26, 2013, 8:46:36 AM6/26/13
to std-dis...@isocpp.org
The first that comes in head are constexpr constructors.

среда, 26 июня 2013 г., 16:43:55 UTC+4 пользователь Ville Voutilainen написал:

joerg....@pdv-fs.de

unread,
Jun 26, 2013, 8:50:03 AM6/26/13
to std-dis...@isocpp.org

Why should it be allowed? Is this for some code generation solution or some such?


Our logging facility provides a macro RETURN that logs, among other things, the current line.

It can be used in functions with a result like this: RETURN( value );
Or it can be used in functions with void result like so: RETURN;

And i'd like to use it in constructors (and destructors) to see which return on which line was
taken, if more than one RETURN is possible.

Message has been deleted

Daniel Krügler

unread,
Jun 26, 2013, 8:51:40 AM6/26/13
to std-dis...@isocpp.org
2013/6/26 Vlad from Moscow <vlad....@mail.ru>:
> The first that comes in head are constexpr constructors.

They don't need that requirement, they are already supported.

- Daniel

Ville Voutilainen

unread,
Jun 26, 2013, 8:52:05 AM6/26/13
to std-dis...@isocpp.org
Thanks, seems reasonable. I expect some people will throw a hissy-fit about macros being
a motivating use case, but I don't mind allowing void returns in ctors/dtors.

Vlad from Moscow

unread,
Jun 26, 2013, 8:53:20 AM6/26/13
to std-dis...@isocpp.org
Also for expressions of type void I do not see a difference between
 
        A()
        {
          func();
          return;
        }
and
 
        A()
        {
          return func();
        }

среда, 26 июня 2013 г., 16:43:55 UTC+4 пользователь Ville Voutilainen написал:

David Rodríguez Ibeas

unread,
Jun 26, 2013, 9:12:45 AM6/26/13
to std-dis...@isocpp.org
While I won't make a fuss about macros, the question that pops to mind is whether the implementation of the macro is the most appropriate and whether it would not be feasible to rewrite it, say to:

#define RETURN do { func(); return; } while (false)

That would not require a change in the standard. Of course I imagine that the original macro is more complicated and it might not be as simple as the code above... but I have the feeling something like that should be feasible.

    David


joerg....@pdv-fs.de

unread,
Jun 26, 2013, 9:21:35 AM6/26/13
to std-dis...@isocpp.org, dib...@ieee.org
Am Mittwoch, 26. Juni 2013 15:12:45 UTC+2 schrieb David Rodríguez Ibeas:
While I won't make a fuss about macros, the question that pops to mind is whether the implementation of the macro is the most appropriate and whether it would not be feasible to rewrite it, say to:

#define RETURN do { func(); return; } while (false)

That would not require a change in the standard. Of course I imagine that the original macro is more complicated and it might not be as simple as the code above... but I have the feeling something like that should be feasible.

    David

The func() example was just a simplified case. The macro is a little more complex than this.
Remember that you have to handle the case RETURN(value);

In the above case this expands to "{ func(); return; } while (false)(value)". Which wont compile.

But this is off topic for this thread. I hope my macro usage wont drag this discussion in the wrong direction.

David Rodríguez Ibeas

unread,
Jun 26, 2013, 9:57:09 AM6/26/13
to std-dis...@isocpp.org
Agreed that the exact details of the macro should not drag the discussion away. I was just trying to point out that it is my opinion that you are proposing a change to the standard that does not enable any new feature. That is, I find (again, opinion and I am not in the committee :) that there is little added value in enabling that.

My understanding is that the motivation for allowing 'return f();' where 'f' has a void return type is to simplify generic code and not have to detect whether 'f' would return a value or not and then special case the templates.

// Excuse the syntax errors, I am not that familiar with variadic templates, but hopefully you get the idea
template <typename F, typename... Args>
auto logforwarder(F f, Args&&... args) -> decltype(f(std::forward(args)...)) {
    // do some logging
    return f(std::forward(args)...);
}

We don't need to detect whether 'decltype(f(args...))' is void to provide a different implementation that does:

  // ... duplicate all other code, select the alternative with SFINAE
  f(std::forward(args)...);
  return;
}

But in the case that you are proposing, the type of 'f' does not matter at all, you are not allowed to return a value from a constructor, so no matter how generic the code 'return f(std::forward(args)...);' is ill-formed. What you are asking for is an exception to the rule.

Stated in a different way, for regular functions, enabling 'X g() { return f(); }' creates uniformity. No matter what 'X' is (including void) the syntax is uniform, you don't need to special case. Allowing 'T::T() { return f(); }' breaks uniformity by creating a special case in which a return statement inside a constructor can take an argument, but only if 'f' has a void return type.

My personal opinion is that the less special cases and exceptions the simpler the language. And in this case I don't see a strong enough motivating example for an exception. From a high level perspective, there are multiple alternatives, like having to macros, one that returns a value and a different one that does not return a value:

#define VRETURN do { /* do the logging here */; return; } while (false);

Then again, there might be other use cases that might be a better motivation for adding an exception to the language, it is just that I fail to see them.

    David


joerg....@pdv-fs.de

unread,
Jun 26, 2013, 10:20:52 AM6/26/13
to std-dis...@isocpp.org, dib...@ieee.org
Am Mittwoch, 26. Juni 2013 15:57:09 UTC+2 schrieb David Rodríguez Ibeas:

What you are asking for is an exception to the rule.

I can see your point.

My primary motivation is not the macro itself but the difference between gcc and clang.
We have developers using clang and some using gcc. And I like to reduce the cases where
both compilers behave different.

So I am comfortable with this or that decision. (Well, more comfortable with this) But now both
compiler writers can point to the ambiguous standard. If it is ambiguous, I am not sure on this.

   Jörg

Nevin Liber

unread,
Jun 26, 2013, 10:25:46 AM6/26/13
to std-dis...@isocpp.org
On 26 June 2013 08:57, David Rodríguez Ibeas <dib...@ieee.org> wrote:
But in the case that you are proposing, the type of 'f' does not matter at all, you are not allowed to return a value from a constructor, so no matter how generic the code 'return f(std::forward(args)...);' is ill-formed. What you are asking for is an exception to the rule.

Stated in a different way, for regular functions, enabling 'X g() { return f(); }' creates uniformity. No matter what 'X' is (including void) the syntax is uniform, you don't need to special case. Allowing 'T::T() { return f(); }' breaks uniformity by creating a special case in which a return statement inside a constructor can take an argument, but only if 'f' has a void return type.

It is more uniform to allow both:

void f() { return g(); }

and

h::h() { return g(); }

than not to.

+1 for the idea.
 
My personal opinion is that the less special cases and exceptions the simpler the language. And in this case I don't see a strong enough motivating example for an exception.

Right now constructors not allowing return with an argument *is* a special case.  It makes it far more special than it has to be.
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com(847) 691-1404

Ville Voutilainen

unread,
Jun 26, 2013, 10:26:45 AM6/26/13
to std-dis...@isocpp.org
On 26 June 2013 17:20, <joerg....@pdv-fs.de> wrote:
Am Mittwoch, 26. Juni 2013 15:57:09 UTC+2 schrieb David Rodríguez Ibeas:


What you are asking for is an exception to the rule.

I can see your point.

One could argue that the current prohibition to return anything in a constructor is already
an exception to the rules for functions, and this change would remove an exception rather
than add one.


David Rodríguez Ibeas

unread,
Jun 26, 2013, 11:08:49 AM6/26/13
to std-dis...@isocpp.org
I am surprised on the comments that the exception here are constructors not returning anything vs. functions being able to return. Constructors are special beasts, there are many rules that are different in constructors and the rest of the code, at the lowest level the differences would include the fact that they might be implicitly declared/defined or the existence of initialization lists. From the user point of view you cannot call them directly, they are either called by the compiler or by means of 'new' (placement-new).

Focusing on the possibility of returning a value, when declaring the constructor the syntax does not allow to declare a return type which is consistent with the definition not being able to return one, when the constructor is executed there is no possible way of retrieve a value.

While in the lowest possible level the constructor will be transformed by the compiler into a function, at the C++ level constructors and destructors are very different from functions at many different levels. At this point constructors are a subcategory of functions with a consistent set of rules, and the suggestion is adding one more corner case. Where we currently say "constructors cannot return a value" the proposal is to add a tail "... except when the value is void". We are creating an exception within the rules for constructors.

The prohibition of returning anything from a constructor is (personal opinion) not an exception to the rules of functions, but one of the traits that make constructors different than the other functions. And there are more such differences than I have enumerated here.

In standardese constructors are 'special' member functions it is expected that 'special' will be different from 'normal' functions.

   David

Disclaimer: This is just my opinion. 



Not really, even from that point of view, this would not remove an exception, just substitute one exception for another. 

Nevin Liber

unread,
Jun 26, 2013, 11:24:07 AM6/26/13
to std-dis...@isocpp.org
On 26 June 2013 10:08, David Rodríguez Ibeas <dib...@ieee.org> wrote:
I am surprised on the comments that the exception here are constructors not returning anything vs. functions being able to return.

void functions don't return anything either.  Other than (what is probably) a historical accident, why should the rules for the bodies of those functions be any different?  This is just one more inconsistency that makes the language a little more difficult to learn and use.
 
Constructors are special beasts, there are many rules that are different in constructors and the rest of the code,

And it would be more uniform if we could minimize those differences.  This proposal helps that.
 
Focusing on the possibility of returning a value, when declaring the constructor the syntax does not allow to declare a return type which is consistent with the definition not being able to return one, when the constructor is executed there is no possible way of retrieve a value.

I don't see how that is any different than a void function.
 
While in the lowest possible level the constructor will be transformed by the compiler into a function, at the C++ level constructors and destructors are very different from functions at many different levels. At this point constructors are a subcategory of functions with a consistent set of rules,

Consistent with respect to what?
 
and the suggestion is adding one more corner case. Where we currently say "constructors cannot return a value" the proposal is to add a tail "... except when the value is void".

Why should the rules be different than with void functions, other than "we can"?
 
We are creating an exception within the rules for constructors.

I just don't understand this.  If we change the rules for constructors, it won't be an exception within the rules of constructors, pretty much by definition.

This way it'll be one less exception within the rules for bodies of functions, whether they be constructors or not.

David Rodríguez Ibeas

unread,
Jun 26, 2013, 1:02:44 PM6/26/13
to std-dis...@isocpp.org
I guess I did not get my point through...


On Wed, Jun 26, 2013 at 11:24 AM, Nevin Liber <ne...@eviloverlord.com> wrote:
On 26 June 2013 10:08, David Rodríguez Ibeas <dib...@ieee.org> wrote:
I am surprised on the comments that the exception here are constructors not returning anything vs. functions being able to return.

void functions don't return anything either.  Other than (what is probably) a historical accident, why should the rules for the bodies of those functions be any different?  This is just one more inconsistency that makes the language a little more difficult to learn and use.
 
Constructors are special beasts, there are many rules that are different in constructors and the rest of the code,

And it would be more uniform if we could minimize those differences.  This proposal helps that.

Does it? If you consider a constructor equivalent to a void function then this should be allowed:

struct U {};
struct T { T(); };
T::T() { return U(); }

Since that is allowed in the case of void functions in the case of functions:

void f();
void g() { return f(); } 

Although the semantics are completely different, and they are different because constructors are not functions and you cannot call a constructor, so the syntax means a completely different thing: return an rvalue of type U default constructed.

My point is that constructors are not regular functions, so differences between constructors and regular functions are expected.

The rationale (if I understood it correctly) of allowing returning 'nothing' (i.e. returning void) was to support generic programming as in the example I provided before. That rationale does not apply here. If I am mistaken and there is a different rationale for 6.6.3/3 then disregard all this rant :)

 
Focusing on the possibility of returning a value, when declaring the constructor the syntax does not allow to declare a return type which is consistent with the definition not being able to return one, when the constructor is executed there is no possible way of retrieve a value.

I don't see how that is any different than a void function.

Well, void functions have a return type (void) and the grammar for function invocation allows the user to access the returned value in case there is such. A function invocation is an expression of type void. There is no *constructor invocation* syntax.
 
While in the lowest possible level the constructor will be transformed by the compiler into a function, at the C++ level constructors and destructors are very different from functions at many different levels. At this point constructors are a subcategory of functions with a consistent set of rules,

Consistent with respect to what?

Consistent in that inside constructors you cannot do 'return X;' for any 'X' (other than a macro that expands to nothing).  Single rule 6.6.3/2 for constructors, without an exception added in 6.6.3/3.

and the suggestion is adding one more corner case. Where we currently say "constructors cannot return a value" the proposal is to add a tail "... except when the value is void".

Why should the rules be different than with void functions, other than "we can"?

They don't need to be different, why should the rules be the same? Why change what we have?
 
We are creating an exception within the rules for constructors.

I just don't understand this.  If we change the rules for constructors, it won't be an exception within the rules of constructors, pretty much by definition.

This way it'll be one less exception within the rules for bodies of functions, whether they be constructors or not.

Not really, it will be a different exception. You trade: "constructors can only return with no value" for "constructors can return with no value or an expression of type 'void'".

I am not sure how to emphasize this more... in my point of view constructors are NOT functions. A function has a (possibly cv-void) return type, and can return a possibly void expression. Constructors have no return type, and cannot return (even a void) expression. The rules as they are make more sense than: constructors don't have a return type but can return things as long as those things are void.

In my mind there is no need to make constructors more normal-function-like, they are different beasts, they implicitly call other constructors, they might be implicitly defined by the compiler. For functions there is a symmetry between the return type and what can be returned, and that symmetry is also present in constructors: they don't have a return type, they cannot return anything. Enabling the return of a void expression would break that symmetry. Will this change make constructors more of a normal function? No, you cannot call them with function-like syntax, you cannot take the address, etc. you can just make them like a void function for the return statement, but that won't make them regular functions. You can paint a lime yellow, but that won't make it a lemon.

By the way, please note that a) I might have an opinion but no voting rights, b) reading my emails I feel that it seems that my position is much stronger than what it really is. While I believe that this change won't add value, I don't think hell will break loose either way :)

Johannes Schaub

unread,
Jun 26, 2013, 2:21:31 PM6/26/13
to std-dis...@isocpp.org


Am 26.06.2013 19:02 schrieb "David Rodríguez Ibeas" <dib...@ieee.org>:
>
> I guess I did not get my point through...
>
>
> On Wed, Jun 26, 2013 at 11:24 AM, Nevin Liber <ne...@eviloverlord.com> wrote:
>>
>> On 26 June 2013 10:08, David Rodríguez Ibeas <dib...@ieee.org> wrote:
>>>
>>> I am surprised on the comments that the exception here are constructors not returning anything vs. functions being able to return.
>>
>>
>> void functions don't return anything either.  Other than (what is probably) a historical accident, why should the rules for the bodies of those functions be any different?  This is just one more inconsistency that makes the language a little more difficult to learn and use.
>>  
>>>
>>> Constructors are special beasts, there are many rules that are different in constructors and the rest of the code,
>>
>>
>> And it would be more uniform if we could minimize those differences.  This proposal helps that.
>
>
> Does it? If you consider a constructor equivalent to a void function then this should be allowed:
>
> struct U {};
> struct T { T(); };
> T::T() { return U(); }
>

This incorrect. The expression "U ()" is not only a call to Us constructor, but does considerably more. It just contains an implicit call to the constructor as an subexpression. The type of "U ()" is U.

Supporting this would actually erase a hole in the type system. Currently a statement like "a function has a return type" is incorrect because the function type of a constructor is not defined.

> Since that is allowed in the case of void functions in the case of functions:
>
> void f();
> void g() { return f(); } 
>

This comparison is therefor unsound.

> Although the semantics are completely different, and they are different because constructors are not functions and you cannot call a constructor,

Incorrect. They are functions. You just cannot call them directly. Directly refering to the constructor of U is done by U::U (which is allowed in declarative contexts such as friend declarations), not by a cast notation.

>
> My point is that constructors are not regular functions, so differences between constructors and regular functions are expected.
>

Conversion functions are special aswell, still you can say

    operator void () {
      return void ("folks");
    }

> The rationale (if I understood it correctly) of allowing returning 'nothing' (i.e. returning void) was to support generic programming as in the example I provided before. That rationale does not apply here. If I am mistaken and there is a different rationale for 6.6.3/3 then disregard all this rant :)
>

In this thread it was shown that the rationale applies here aswell.

Johannes Schaub

unread,
Jun 26, 2013, 3:17:05 PM6/26/13
to std-dis...@isocpp.org
2013/6/26 David Rodríguez Ibeas <dib...@ieee.org>:
> While in the lowest possible level the constructor will be transformed by
> the compiler into a function, at the C++ level constructors and destructors
> are very different from functions at many different levels. At this point
> constructors are a subcategory of functions with a consistent set of rules,
> and the suggestion is adding one more corner case. Where we currently say
> "constructors cannot return a value" the proposal is to add a tail "...
> except when the value is void". We are creating an exception within the
> rules for constructors.
>

On the contrary, we can simplify things. 12.1p1 can replace " A return
statement in the body of a constructor shall not specify a return
value." by "A constructor's return type implicitly is void.".

If we do the same for destructors, we can then also take away "If the
postfix-expression designates a destructor (12.4), the type of the
function call expression is void;" from 5.2.2p3 (which was inserted as
response to a DR I made only recently).

We can also remove ", a constructor (12.1), or a destructor (12.4)." in 6.6.3p2.

David Rodríguez Ibeas

unread,
Jun 26, 2013, 3:18:24 PM6/26/13
to std-dis...@isocpp.org
On Wed, Jun 26, 2013 at 2:21 PM, Johannes Schaub <schaub....@googlemail.com> wrote:

This incorrect. The expression "U ()" is not only a call to Us constructor, but does considerably more. It just contains an implicit call to the constructor as an subexpression. The type of "U ()" is U.

I know this, I should have made it more explicit in the original email, which is part of the point, might look like a function cannot be called like one. 

Supporting this would actually erase a hole in the type system. Currently a statement like "a function has a return type" is incorrect because the function type of a constructor is not defined.

Are you saying that there is a hole in the type system because constructors don't have a return type? If I have not been clear enough, from my point of view constructors are *not* regular functions, maybe it would be simpler if they were called something different than 'special member function' 

Incorrect. They are functions. You just cannot call them directly. Directly refering to the constructor of U is done by U::U (which is allowed in declarative contexts such as friend declarations), not by a cast notation.

They are 'special' functions, and have many differences to regular functions. They don't have a return type, you cannot call them, you cannot take the address, there are (potentially) multiple symbols generated for each constructor, they have an initialization lies, the compiler injects code that you don't write... there are almost more differences than similarities.

Constructors are 'things' that transform memory into objects (and happen to be called 'special member functions' in the standard), while the rest of the 'normal' member functions are applied to objects.
 

the example I provided before. That rationale does not apply here. If I am mistaken and there is a different rationale for 6.6.3/3 then disregard all this rant :)

In this thread it was shown that the rationale applies here aswell.

What is the rationale for allowing the returning of an expression of type void in a function returning void? I am not saying it has not been stated in this thread, but I have failed to recognize it.

If the rationale you refer to is writing generic code where the type and thus avoiding the need to provide specializations, then I don't see how it applies here, regardless of the type arguments to a constructor, the expression 'return X;' inside the body of the constructor is at the very least suspicious and as I understand it incorrect in all cases in the current standard.

Richard Smith

unread,
Jun 26, 2013, 3:32:52 PM6/26/13
to std-dis...@isocpp.org
+1. I find it surprising that the example in the original message is
invalid, and can see no reason to reject it. It seems completely
inconsistent to me that there exist contexts in which "return;" is
valid but "return void();" is not valid.

wolfei...@gmail.com

unread,
Jun 29, 2013, 12:44:39 PM6/29/13
to std-dis...@isocpp.org
I agree. For a function that does not return a value, return; and return void(); should be equal and legal. This is just as true for constructors and destructors that don't return a value as regular functions. Obviously, there are lots of differences between functions and constructors, but there is no need for this to be one of them.
Reply all
Reply to author
Forward
0 new messages