Why does g ++ complete the initialization of std :: function <> from a type with a conversion operator and inaccessible function call statements?

This code does not work with g ++ 4.9 and later (including the build from svn current), but compiles without warning using the clang ++ compiler and microsofts (from VS2015.)

 #include <functional> struct A { void operator()() const {} }; struct B { void operator()() const {} }; struct C : private A, private B { operator std::function<void()>() const { return nullptr; } }; int main() { std::function<void()> f{C{}}; } 

The f construct in main() fails because operator() is ambiguous in struct C

Why does g ++ find this ambiguous? Function call statements in C privately inherited and unavailable. Adding a closed or explicitly remote, void operator()() const to struct C does the code compilation and uses the conversion operator as intended. Why can't these inaccessible operators cause problems if legacy operators are unavailable?

+7
source share
3 answers

Constructor: template<class F> function(F f);

C ++ 11:

f must be Callable for argument types ArgTypes and return type R

C ++ 14:

Do not participate in overload resolution if f does not match Callable for argument types ArgTypes... and returns type R


In C ++ 11, this constructor pattern is a better match than a transform sequence that includes the std::function move constructor and your custom transform operator. Thus, overload resolution selects a constructor template, which then does not compile because f not Callable .

In C ++ 14, the constructor pattern is prone to change replacements, since f not Callable . Thus, the constructor template is not involved in overload resolution, and the best remaining match is the conversion sequence, including the std::function move constructor and your custom transform operator, which is therefore used.


Clang compiles your test file in both C ++ 11 and C ++ 14. GCC rejects your test in both C ++ 11 and C ++ 14. Here is another test case that demonstrates the same problem in GCC :

 #include <type_traits> struct A { void operator()() const {} }; struct B { void operator()() const {} }; struct C : A, B {}; template <typename F, typename = std::result_of_t<F&()>> void test(int) {} template <typename F> void test(double) {} int main() { test<C>(42); } 

test(int) should not be involved in overload resolution, because std::result_of_t<F&()> should be a replacement change, so test(double) should be called. However, this code does not compile in GCC.

This is the same problem as in your test case because it is the same mechanism used to implement SFINAE in the std::function constructor std::function in libstdC ++.

+3
source

The difference between clang ++ and g ++ seems to be related to some fuzziness of SFINAE :

...

Replacement failure - any situation where the type or expression above would be poorly formed (with the necessary diagnostics) if they were written using replaced arguments.

Only errors in types and expressions in the context of the function type or its parameter types of the SFINAE Errors template. If evaluating a substituted type / expression causes a side effect, such as instantiating a specialization template, generating an implicitly defined member function, etc. errors in these side effects are regarded as hard errors .

Now std::function<R(Args...)> provides a template constructor

 template<class F> function( F f ); 

which (through the use of SFINAE)

does not participate in overload resolution if f Callable for the argument types Args... and return type R

struct C from your example is not a Callable type due to the ambiguity of operator() (the fact that the latter is not available in C does not play any role at this stage of overload resolution), Thus, depending on the implementation of the is-this-type-callable check is-this-type-callable in the standard library your code may or may not compile . This may explain the difference between g ++ and msvc, but not the difference between clang ++ and g ++, which, using the same standard library, work differently - clang ++ compiles your code, but g ++ does not. I do not exclude the possibility that the C ++ standard strictly determines which one is correct, but IMHO does not exceed what most C ++ programmers should be able to independently determine.

+2
source

In C ++ 11, std::function has a generic constructor

 template<class F> function( F f ) 

that blindly tries to treat f as a called object.

It is selected (rather greedily) if the other constructors are not.

private databases should not interrupt your code.

In C ++ 14, there are rules about this overload that are considered only in some cases. It seems your C ++ 11 compiler applies such rules, but personal inheritance confuses him. This, however, is beyond the scope of the question.

Here are 1 arg std::function constructors:

 function( std::nullptr_t ); function( const function& other ); function( function&& other ); template< class F > function( F f ); 

The one you want

 function( function&& other ); 

user-defined conversion by

 template< class F > function( F f ); 

therefore this one should be applied and then not compiled.

+1
source

All Articles