Should std :: function assign ignore the return type?

Is the code below valid C ++ in accordance with the C ++ 11 or C ++ 14 standard?

#include <functional> int ReturnInt() { return 5; } int main( int argc, char **argv ) { std::function< void () > BoundType = ReturnInt; return 0; } 

The code compiles with the latest versions of cygwin gcc (4.8.3) and clang (4.3.2), but not with Visual Studio 2013, Visual Studio November 2013 CTP or a preview of Visual Studio 14. It also compiles on all platforms if std :: function changed to boost :: function.

I found this other question which suggests that it should work.

+6
source share
2 answers

The code is undefined behavior in C ++ 11 and poorly formed in C ++ 14. C ++ 14 adds this remark to the specification of this constructor:

Notes: These constructors should not participate in overloading if f is callable (09/20/11) for argument types ArgTypes... and return type is R

The called is defined in [func.wrap.func] / p2:

A called object f type f is available for argument types ArgTypes and return type R if the expression INVOKE (f, declval<ArgTypes>()..., R) , considered as an unvalued operand (Clause 5), is well formed (20.9.2 )

For this INVOKE to be well-formed, the return INVOKE type without R must be implicitly converted to R ([func.require] / p2).

In C ++ 11, these statements were under the Requries proposal, which means that the client must receive them correctly, and if the client fails, everything can happen, including a successful compilation.

This has been modified by LWG 2132 .

+9
source

std::function in C ++ 11 and 14 does not have the desired behavior.

It also prevents SFINAE from detecting bad overloads.

We can wrap it with another type that has the desired behavior ( void throwing back) and has poor SFINAE overload detection while we are on it as follows:

 template<class Sig> struct checked_function; template<class R, class... Args> struct checked_function<R(Args...)>:std::function<R(Args...)> { using function = std::function<R(Args...)>; checked_function(std::nullptr_t):function() {} checked_function():function() {} template<class F, class=typename std::enable_if< std::is_convertible< typename std::result_of< F(Args...) >::type , R >::value >::type> checked_function( F&& f ):function( std::forward<F>(f) ) {} template<class F, class=typename std::enable_if< std::is_convertible< typename std::result_of< F(Args...) >::type , R >::value >::type> checked_function& operator=( F&& f ) { return function::operator=( std::forward<F>(f) ); } checked_function& operator=( checked_function const& o ) = default; checked_function& operator=( checked_function && o ) = default; checked_function( checked_function const& o ) = default; checked_function( checked_function && o ) = default; }; template<class... Args> struct checked_function<void(Args...)>:std::function<void(Args...)> { using function = std::function<void(Args...)>; checked_function(std::nullptr_t):function() {} checked_function():function() {} template<class F, class=typename std::enable_if< std::is_same< typename std::result_of< F(Args...) >::type , void >::value >::type> checked_function( F&& f, int*unused=nullptr ):function( std::forward<F>(f) ) {} template<class F> static auto wrap(F&& f){ return [f_=std::forward<F>(f)](auto&&...args){ f_( std::forward<decltype(args)>(args)... ); }; } template<class F, class=typename std::enable_if< !std::is_same< typename std::result_of< F(Args...) >::type , void >::value >::type> checked_function( F&& f, void*unused=nullptr ): function( wrap(std::forward<F>(f)) ) {} template<class F> typename std::enable_if< !std::is_same< typename std::result_of< F(Args...) >::type , void >::value, checked_function& >::type operator=( F&& f ) { return function::operator=( wrap(std::forward<F>(f)) ); } template<class F> typename std::enable_if< std::is_same< typename std::result_of< F(Args...) >::type , void >::value, checked_function& >::type operator=( F&& f ) { return function::operator=( std::forward<F>(f) ); } checked_function& operator=( checked_function const& o ) = default; checked_function& operator=( checked_function && o ) = default; checked_function( checked_function const& o ) = default; checked_function( checked_function && o ) = default; }; 

Now it compiles in C ++ 14 (not in C ++ 11, due to wrap : wrap can be replaced at the dial-peer with a copy of its own body, therefore ...). Perhaps it is possible to reduce the pattern using a beam.

It uses some features of C ++ 14 (in particular, in wrap ), you can do this by adding more templates).

Not yet launched.

+3
source

Source: https://habr.com/ru/post/974032/


All Articles