9

The boost::function FAQ item 3 specifically addresses the scenario I am interested in:

Why are there workarounds for void returns? C++ allows them! Void returns are permitted by the C++ standard, as in this code snippet:

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

This is a valid usage of boost::function because void returns are not used. With void returns, we would attempting to compile ill-formed code similar to:

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

In essence, not using void returns allows boost::function to swallow a return value. This is consistent with allowing the user to assign and invoke functions and function objects with parameters that don't exactly match.

Unfortunately, this doesn't work in VS2008:

int Foo();
std::tr1::function<void()> Bar = Foo;

This produces errors starting with:

c:\Program Files\Microsoft Visual Studio 9.0\VC\include\xxcallfun(7) : error C2562: 'std::tr1::_Callable_fun<_Ty>::_ApplyX' : 'void' function returning a value

Is this a failing of the VS2008 TR1 implementation? Does this work in VS2010? Does TR1 address this capability? How about C++0x?

Chris
  • 103
  • 3
  • 2
    `void g() { return f(); }` is allowed to support template code where the return type of the function might not be known in advance... it is still an error for `f()` to actually return a value if `g()` does not. Given this, I would not be surprised to find that the standards comittee elected to diverge from Boost's implementation in this regard, since it provides a more intuitive behavior. But since gcc *does* allow this behavior, I'm not positive. – Dennis Zickefoose Jul 08 '11 at 19:10

1 Answers1

8

I believe tr1 addresses this issue. N1836 (the latest tr1 draft) says:

A function object f of type F is Callable for argument types T1, T2, ..., TN and a return type R, if, given lvalues t1, t2, ..., tNoftypesT1, T2, ..., TN,respectively,INVOKE(f, t1, t2, ..., tN)is well-formed([3.3]) and, if R is not void, convertible to R.

In your example R is void, and so the last part of the requirements for Callable (convertible to R) is ignored.

However it looks like C++0x (C++11) changes the rules. In C++11 Callable is defined as INVOKE(f, t1, t2, ..., tN, R) which is defined in [func.require] as requiring INVOKE(f, t1, t2, ..., tN) to be implicitly convertible to R, with no exception for when R is void. So in C++11, your example should fail.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 2
    Do you know if there is a rationale for this change/regression? If not, it sounds like a defect. – James McNellis Jul 08 '11 at 20:37
  • This indeed sounds weird. No expression is implicitly convertible to `void`. Not even an expression of type `void` (according to clause 4 paragraph 3). One needs an explicit cast to convert things to `void`. I suppose that the intent is that only if the result type is different from `R`, then the expression is implicitly converted to `R`. Then the result could still have the type `void` when `R` is `void`. It's easy to be tricked by those paragraphs though, IMO :) – Johannes Schaub - litb Jul 08 '11 at 20:49