Can tr1::function swallow return values?
Asked Answered
V

1

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?

Vasiliu answered 8/7, 2011 at 17:59 Comment(1)
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.Kootenay
T
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.

Totalitarian answered 8/7, 2011 at 19:9 Comment(2)
Do you know if there is a rationale for this change/regression? If not, it sounds like a defect.Appraise
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 :)Reminiscence

© 2022 - 2024 — McMap. All rights reserved.