This code fails with g++ 4.9 and later (including build from svn current,) but compiles without warning with clang++ and microsofts compiler (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 construction of f
in main()
fails due to operator()
being ambiguous in struct C
.
Why does g++ consider this to be ambiguous? The function call operators in C
are privately inherited and not accessible.
Adding a private, or explicitly deleted, void operator()() const
to struct C
makes the code compile and use the conversion operator as intended. Why would these inaccessible operators not cause problems when the inaccessible inherited ones do?
The static_cast operator converts variable j to type float . This allows the compiler to generate a division with an answer of type float . All static_cast operators resolve at compile time and do not remove any const or volatile modifiers.
Conversion Operators in C++ C++ supports object oriented design. So we can create classes of some real world objects as concrete types. Sometimes we need to convert some concrete type objects to some other type objects or some primitive datatypes. To make this conversion we can use conversion operator.
static_cast is actually an operator, not a function.
Constructor: template<class F> function(F f);
C++11:
f
shall beCallable
for argument typesArgTypes
and return typeR
.
C++14:
Shall not participate in overload resolution unless
f
isCallable
for argument typesArgTypes...
and return typeR
.
In C++11, this constructor template is a better match than the conversion sequence involving the std::function
move constructor and your user-defined conversion operator. So overload resolution selects the constructor template, which then fails to compile because f
is not Callable
.
In C++14, the constructor template is subject to a substitution failure, because f
is not Callable
. So the constructor template does not participate in overload resolution, and the best remaining match is the conversion sequence involving the std::function
move constructor and your user-defined conversion operator, which is therefore used.
Clang compiles your testcase in both C++11 and C++14 mode. GCC rejects your testcase in both C++11 and C++14 mode. Here is another testcase 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 participate in overload resolution because std::result_of_t<F&()>
should be a substitution failure, so test(double)
should be called. However, this code fails to compile in GCC.
This is the same problem seen in your testcase, because this is the same mechanism used to implement SFINAE in the constructor template of std::function
in libstdc++.
The difference between clang++ and g++ seems to be because of some fuzziness of SFINAE:
...
A substitution failure is any situation when the type or expression above would be ill-formed (with a required diagnostic), if written using the substituted arguments.
Only the failures in the types and expressions in the immediate context of the function type or its template parameter types are SFINAE errors. If the evaluation of a substituted type/expression causes a side-effect such as instantiation of some template specialization, generation of an implicitly-defined member function, etc, errors in those side-effects are treated as hard errors.
Now, std::function<R(Args...)>
provides a template constructor
template<class F> function( F f );
which (through utilization of SFINAE)
does not participate in overload resolution unless
f
isCallable
for argument typesArgs...
and return typeR
.
struct C
from your example is not a Callable
type, because of the ambiguity of operator()
(the fact that the latter is inaccessible in C
doesn't play any role at this stage of overload resolution). Thus, depending on the implementation of the is-this-type-callable
check in the Standard Library your code may or may not fail to compile. This can 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 and g++ doesn't. I don't exclude the possibility that the C++ standard defines strictly which of them is correct, however IMHO it is beyond what most C++ programmers should be able to figure out on their own.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With