I noticed some strange results with std::is_convertible and std::is_assignable when std::function object and std::bind are involved.
I would assume that when these functions return true, the conversion can be made. Or am I missing something?
The following code prints different results on different compilers, and I would expect it to print 0 since these types cannot be assigned.
#include <type_traits>
#include <functional>
#include <iostream>
int main()
{
std::cout << std::is_convertible<std::function<void(int)>, std::function<void()>>::value << std::endl;
}
It prints 0 on the following compilers:
gcc 4.8 and gcc 4.9
clang 3.4 (but not the one from ubuntu 12.04)
It prints 1 on the following compilers:
gcc 4.7
VC++12 (VS2013)
clang 3.2
Is there any correct answer?
Are these bugs in compilers or am I messing with stuff that is compiler specific?
In C++11, std::function's constructor taking an arbitrary functor type is specified as (quoting N3337 §20.8.11.2.1 [func.wrap.func.con]/p7):
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires: F shall be CopyConstructible. f shall be Callable (20.8.11.2) for argument types ArgTypes and return type R. The
copy constructor and destructor of A shall not throw exceptions.
Violation of a Requires clause (passing an f not Callable for argument types ArgTypes and return type R) is undefined behavior, so the library is free to do whatever it wants in that case. The library may take the constructor out of overload resolution, but it doesn't have to, and if it doesn't, then you will have issues with overload resolution and std::is_convertible- it will report that pretty much everything under the sun is convertible to std::function (including stuff like double!).
Hence, in LWG issue 2132, the standard was modified to require implementations to remove these constructors from overload resolution (via SFINAE or a similar technique) if the functor isn't Callable for the specified argument types and return type. It now reads:
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires: F shall be CopyConstructible.
8 Remarks: These constructors shall not participate in overload
resolution unless f is Callable (20.9.11.2) for argument types
ArgTypes... and return type R.
So if your standard library implements this resolution, then std::is_convertible<std::function<void(int)>, std::function<void()>>::value is false. Otherwise, it's implementation-dependent.
I would assume that when these functions return true, the conversion can be made. Or am I missing something?
Type traits such as std::is_convertible, std::is_constructible, or std::is_assignable only considers the immediate context - i.e., whether there's a matching function signature that's accessible and not deleted. They don't check if the function's body would compile when instantiated.
Related
Having the following simple code:
#include <concepts>
auto f(const auto&) { }
auto f(std::integral auto) {}
int main()
{
f(5);
}
We have an ambiguous call with clang & gcc but MSVC chooses the more constrained one. So far I found nothing that would support the clang's & gcc's behavior. So is it a bug in both compilers or there is something that makes this call ambiguous?
Without considering constraints, the call is ambiguous because the first overload is deduced to a function parameter type const int& and the second to int. Neither will be considered better than the other when called with a prvalue of type int and neither const auto& or auto are more specialized in usual partial ordering of templates either.
According to [temp.func.order]/6.2.2 constraints on function templates are not taken into account if the types of the function parameters after substitution from template argument deduction do not correspond.
Here the first overload deduced the function parameter to const int& and the second to int. These are not equal. And so partial ordering of the templates will not consider either more specialized than the other based on any constraints either.
Consider the following code, which mixes a member and a non-member operator|
template <typename T>
struct S
{
template <typename U>
void operator|(U)
{}
};
template <typename T>
void operator|(S<T>,int) {}
int main()
{
S<int>() | 42;
}
In Clang this code fails to compile stating that the call to operator| is ambiguous, while gcc and msvc can compile it. I would expect the non-member overload to be selected since it is more specialized. What is the behavior mandated by the standard? Is this a bug in Clang?
(Note that moving the operator template outside the struct resolves the ambiguity.)
I do believe clang is correct marking the call as ambiguous.
Converting the member to a free-standing function
First, the following snippet is NOT equal to the code you have posted w.r.t S<int>() | 42; call.
template <typename T>
struct S
{
};
template <typename T,typename U>
void operator|(S<T>,U)
{}
template <typename T>
void operator|(S<T>,int) {}
In this case the now-non-member implementation must deduce both T and U, making the second template more specialized and thus chosen. All compilers agree on this as you have observed.
Overload resolution
Given the code you have posted.
For overload resolution to take place, a name lookup is initiated first. That consists of finding all symbols named operator| in the current context, among all members of S<int>, and the namespaces given by ADL rules which is not relevant here. Crucially, T is resolved at this stage, before overload resolution even happens, it must be. The found symbols are thus
template <typename T> void operator|(S<T>,int)
template <typename U> void S<int>::operator|(U)
For the purpose of picking a better candidate, all member functions are treated as non-members with a special *this parameter. S<int>& in our case.
[over.match.funcs.4]
For implicit object member functions, the type of the implicit object parameter is
(4.1)
“lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier
(4.2)
“rvalue reference to cv X” for functions declared with the && ref-qualifier
where X is the class of which the function is a member and cv is the cv-qualification on the member function declaration.
This leads to:
template <typename T> void operator|(S<T>,int)
template <typename U> void operator|(S<int>&,U)
Looking at this, one might assume that because the second function cannot bind the rvalue used in the call, the first function must be chosen. Nope, there is special rule that covers this:
[over.match.funcs.5][Emphasis mine]
During overload resolution, the implied object argument is indistinguishable from other arguments.
The implicit object parameter, however, retains its identity since no user-defined conversions can be applied to achieve a type match with it.
For implicit object member functions declared without a ref-qualifier, even if the implicit object parameter is not const-qualified, an rvalue can be bound to the parameter as long as in all other respects the argument can be converted to the type of the implicit object parameter.
Due to some other rules, U is deduced to be int, not const int& or int&. S<T> can also be easily deduced as S<int> and S<int> is copyable.
So both candidates are still valid. Furthermore neither is more specialized than the other. I won't go through the process step by step because I am not able to who can blame me if even all the compilers did not get the rules right here. But it is ambiguous exactly for the same reason as foo(42) is for
void foo(int){}
void foo(const int&){}
I.e. there is no preference between copy and reference as long as the reference can bind the value. Which is true in our case even for S<int>& due to the rule above. The resolution is just done for two arguments instead of one.
Consider the following:
template<typename T>
struct C {};
template<typename T, typename U>
void operator +(C<T>&, U);
struct D: C<D> {};
struct E {};
template<typename T>
void operator +(C<T>&, E);
void F() { D d; E e; d + e; }
This code compiles fine on both GCC-7 and Clang-5. The selected overload for operator + is that of struct E.
Now, if the following change takes place:
/* Put `operator +` inside the class. */
template<typename T>
struct C {
template<typename U>
void operator +(U);
};
that is, if operator + is defined inside the class template, instead of outside, then Clang yields ambiguity between both operator +s present in the code. GCC still compiles fine.
Why does this happen? Is this a bug in either GCC or Clang?
This is a bug in gcc; specifically, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53499 .
The problem is that gcc is regarding the implicit object parameter of a class template member function as having a dependent type; that is, during function template partial ordering gcc transforms
C<D>::template<class U> void operator+(U); // #1
into
template<class T, class U> void operator+(C<T>&, U); // #1a (gcc, wrong)
when it should be transformed into
template<class U> void operator+(C<D>&, U); // #1b (clang, correct)
We can see that when compared to your
template<class T> void operator+(C<T>&, E); // #2
#2 is better than the erroneous #1a, but is ambiguous with #1b.
Observe that gcc incorrectly accepts even when C<D> is not a template at all - i.e., when C<D> is a class template full specialization:
template<class> struct C;
struct D;
template<> struct C<D> {
// ...
This is covered by [temp.func.order]/3, with clarification in the example. Note that again, gcc miscompiles that example, incorrectly rejecting it but for the same reason.
Edit: The original version of this answer said that GCC was correct. I now believe that Clang is correct according to the wording of the standard, but I can see how GCC's interpretation could also be correct.
Let's look at your first example, where the two declarations are:
template<typename T, typename U>
void operator +(C<T>&, U);
template<typename T>
void operator +(C<T>&, E);
Both are viable, but it is obvious that the second template is more specialized than the first. So GCC and Clang both resolve the call to the second template. But let's walk through [temp.func.order] to see why, in the wording of the standard, the second template is more specialized.
The partial ordering rules tell us to replace each type template parameter with a unique synthesized type and then perform deduction against the other template. Under this scheme, the first overload type becomes
void(C<X1>&, X2)
and deduction against the second template fails since the latter only accepts E. The second overload type becomes
void(C<X3>&, E)
and deduction against the first template succeeds (with T = X3 and U = E). Since the deduction succeeded in only one direction, the template that accepted the other's transformed type (the first one) is considered less specialized, and thus, the second overload is chosen as the more specialized one.
When the second overload is moved into class C, both overloads are still found and the overload resolution process should apply in exactly the same way. First, the argument list is constructed for both overloads, and since the first overload is a non-static class member, an implied object parameter is inserted. According to [over.match.funcs], the type of that implied object parameter should be "lvalue reference to C<T>" since the function does not have a ref-qualifier. So the two argument lists are both (C<D>&, E). Since this fails to effect a choice between the two overloads, the partial ordering test kicks in again.
The partial ordering test, described in [temp.func.order], also inserts an implied object parameter:
If only one of the function templates M is a non-static member of
some class A, M is considered to have a new first parameter inserted in its function parameter list. Given cv
as the cv-qualifiers of M (if any), the new parameter is of type “rvalue reference to cv A” if the optional
ref-qualifier of M is && or if M has no ref-qualifier and the first parameter of the other template has rvalue
reference type. Otherwise, the new parameter is of type “lvalue reference to cv A”. [ Note: This allows a
non-static member to be ordered with respect to a non-member function and for the results to be equivalent
to the ordering of two equivalent non-members. — end note ]
This is the step where, presumably, GCC and Clang take different interpretations of the standard.
My take: The member operator+ has already been found in the class C<D>. The template parameter T for the class C is not being deduced; it is known because the name lookup process entered the concrete base class C<D> of D. The actual operator+ that is submitted to partial ordering therefore does not have a free T parameter; it is not void operator+(C<T>&, U), but rather, void operator+(C<D>&, U).
Thus, for the member overload, the transformed function type should not be void(C<X1>&, X2), but rather void(C<D>&, X2). For the non-member overload, the transformed function type is still void(C<X3>&, E) as before. But now we see that void(C<D>&, X2) is not a match for the non-member template void(C<T>&, E) nor is void(C<X3>&, E) a match for the member template void(C<D>&, U). So partial ordering fails, and overload resolution returns an ambiguous result.
GCC's decision to continue to select the non-member overload makes sense if you assume that it is constructing the transformed function type for the member lexically, making it still void(C<X1>&, X2), while Clang substitutes D into the template, leaving only U as a free parameter, before beginning the partial ordering test.
I have some trouble understanding the need for std::result_of in C++0x. If I understood correctly, result_of is used to obtain the resulting type of invoking a function object with certain types of parameters. For example:
template <typename F, typename Arg>
typename std::result_of<F(Arg)>::type
invoke(F f, Arg a)
{
return f(a);
}
I don't really see the difference with the following code:
template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(f(a)) //uses the f parameter
{
return f(a);
}
or
template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(F()(a)); //"constructs" an F
{
return f(a);
}
The only problem I can see with these two solutions is that we need to either:
have an instance of the functor to use it in the expression passed to decltype.
know a defined constructor for the functor.
Am I right in thinking that the only difference between decltype and result_of is that the first one needs an expression whereas the second does not?
result_of was introduced in Boost, and then included in TR1, and finally in C++0x. Therefore result_of has an advantage that is backward-compatible (with a suitable library).
decltype is an entirely new thing in C++0x, does not restrict only to return type of a function, and is a language feature.
Anyway, on gcc 4.5, result_of is implemented in terms of decltype:
template<typename _Signature>
class result_of;
template<typename _Functor, typename... _ArgTypes>
struct result_of<_Functor(_ArgTypes...)>
{
typedef
decltype( std::declval<_Functor>()(std::declval<_ArgTypes>()...) )
type;
};
If you need the type of something that isn't something like a function call, std::result_of just doesn't apply. decltype() can give you the type of any expression.
If we restrict ourselves to just the different ways of determining the return type of a function call (between std::result_of_t<F(Args...)> and decltype(std::declval<F>()(std::declval<Args>()...)), then there is a difference.
std::result_of<F(Args...) is defined as:
If the expression
INVOKE (declval<Fn>(), declval<ArgTypes>()...) is well
formed when treated as an
unevaluated operand (Clause 5), the
member typedef type shall name the
type decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...));
otherwise, there shall be no member
type.
The difference between result_of<F(Args..)>::type and decltype(std::declval<F>()(std::declval<Args>()...) is all about that INVOKE. Using declval/decltype directly, in addition to being quite a bit longer to type, is only valid if F is directly callable (a function object type or a function or a function pointer). result_of additionally supports pointers to members functions and pointers to member data.
Initially, using declval/decltype guaranteed a SFINAE-friendly expression, whereas std::result_of could give you a hard error instead of a deduction failure. That has been corrected in C++14: std::result_of is now required to be SFINAE-friendly (thanks to this paper).
So on a conforming C++14 compiler, std::result_of_t<F(Args...)> is strictly superior. It's clearer, shorter, and correctly† supports more Fs‡ .
†Unless, that is, you're using it in a context where you don't want to allow pointers to members, so std::result_of_t would succeed in a case where you might want it to fail.
‡ With exceptions. While it supports pointers to members, result_of will not work if you try to instantiate an invalid type-id. These would include a function returning a function or taking abstract types by value. Ex.:
template <class F, class R = result_of_t<F()>>
R call(F& f) { return f(); }
int answer() { return 42; }
call(answer); // nope
The correct usage would've been result_of_t<F&()>, but that's a detail you don't have to remember with decltype.
I have this code:
#include <iostream>
#include <functional>
struct A
{
int operator()(int i) const {
std::cout << "F: " << i << std::endl;
return i + 1;
}
};
int main()
{
A a;
std::tr1::function<int(int)> f = std::tr1::ref(a);
std::cout << f(6) << std::endl;
}
The aim is to pass the functor object by a reference_wrapper, in a way to avoid useless copy costructor calls.
I expect the following output:
F: 6
7
It works correctly with GCC >= 4.4.0, Visual Studio 2008 and with boost by substituting std::tr1 namespace with boost. It only doesn't work with the new Visual Studio 2010 both Express Beta 2 and Release Candidate.
Are this new C++ features bugged in vs2010?
Or there is some mistake or misuse in the code?
I think i found the reason. This is what TR1 3.4/2 says about result_of<T(A1, A2, ..., AN)>::type, used in the determination of the return type of reference_wrapper<T>::operator():
The implementation may determine the type member via any means that produces the exact type of the expression f(t1, t2, ..., tN) for the given types. [Note: The intent is that implementations are permitted to use special compiler hooks —end note]
And then paragraph 3:
If F is not a function object defined by the standard library, and if either the implementation cannot determine the type of the expression f(t1, t2, ..., tN) or if the expression is ill-formed, the implementation shall use the following process to determine the type member:
If F is a possibly cv-qualified class type with no member named result_type or if typename F::result_type is not a type:
If N=0 (no arguments), type is void.
If N>0, type is typename F::template result<F(T1, T2,..., TN)>::type
The error message is an artefact of trying these fall-backs. Provide a typedef for result_type to int and it should work, i think. Notice that in C++0x, this is different. It does not rely on result_type or a result template, since it can use decltype.
If with <functional> it fails with MSVC10 in C++0x mode, it smells like a bug, i would say. But maybe someone else knows what's going on. It may (but is not guaranteed to) work with <tr1/functional> in C++0x mode if that header chooses to take the decltype way instead of ::result_type. I would typedef result_type - that way i think it should always work regardless of whether the tr1 header is used or the c++0x header.
Also notice that boost::tr1 says in its documentation that it does not support the function call operator (but it merely supports implicit conversions to T&).
I encounter a similar problem here :
Prevent unnecessary copies of C++ functor objects
To make it compile on MSVC10, I had to derive my function object from std::unary_function.