Clang,GCC,MSVC have different opinion about conversion of member functions.
Who is right ?
https://gcc.godbolt.org/z/QNsgwd
template<typename T>
struct a
{
template <typename... Args>
void va(Args...) {}
template <typename X>
void x(X) {}
void y(int) {}
};
struct b : a<b>
{
void testva()
{
using F = void (a<b>::*)();
F f = (F)&a<b>::va<int>; // gcc: error, msvc: error, clang: ok
}
void testx()
{
using F = void (a<b>::*)();
F f = (F)&a<b>::x<int>;// gcc: error, msvc: ok, clang: ok
}
void testy()
{
using F = void (a<b>::*)();
F f = (F)& a<b>::y; // gcc: ok, msvc: ok, clang: ok
}
};
testx and testy are well-formed, so gcc is wrong about testx. But the Standard is somewhat vague about testva.
Starting with the easiest, in testy the expression &a<b>::y names a non-template function which is not overloaded, so it has type void (a<b>::*)(int) without need for further analysis. Conversion from any pointer-to-member-function to any other pointer-to-member-function is a well-formed reinterpret_cast with unspecified results except if converted back to the original type, and a C-style cast can do what a reinterpret_cast can do.
For template functions we have [over.over]/1-2:
A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or a pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. A function with type F is selected for the function type FT of the target type required in the context if F (after possibly applying the function pointer conversion) is identical to FT. The target can be
...
an explicit type conversion ([expr.type.conv], [expr.static.cast], [expr.cast]),
...
If the name is a function template, template argument deduction is done ([temp.deduct.funcaddr]), and if the argument deduction succeeds, the resulting template argument list is used to generate a single function template specialization, which is added to the set of overloaded functions considered. [ Note: As described in [temp.arg.explicit], if deduction fails and the function template name is followed by an explicit template argument list, the template-id is then examined to see whether it identifies a single function template specialization. If it does, the template-id is considered to be an lvalue for that function template specialization. The target type is not used in that determination. — end note ]
So this means we first try template argument deduction for a<b>::x<int>, matching it to the target type void (a<b>::*)(). But there are no specializations that can possibly give an exact match, since they all have one argument, not zero, so deduction fails. But per the note, there's also [temp.arg.explicit] (paragraph 3 in C++17, 4 in the latest C++20 draft):
Trailing template arguments that can be deduced or obtained from default template-arguments may be omitted from the list of explicit template-arguments. A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced as an empty sequence of template arguments. ... In contexts where deduction is done and fails, or in contexts where deduction is not done, if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template specialization.
In testx, the template-id a<b>::x<int> identifies a single function template specialization. So it names that specialization, and again the C-style cast is valid with unspecified result.
So in testva, does a<b>::va<int> identify a single specialization? It would certainly be possible to use that expression to name different specializations, via [temp.arg.explicit]/9:
Template argument deduction can extend the sequence of template arguments corresponding to a template parameter pack, even when the sequence contains explicitly specified template arguments.
Except this says "template argument deduction". And here the template argument deduction involved fails, since it required an impossible match with the target type void (a<b>::*)(). So nothing really explains whether a<b>::va<int> identifies a single specialization, since no other method of getting additional template arguments is described, or identifies multiple specializations, since it could be validly used in other contexts with matching target types.
clang is right
[expr.reinterpret.cast]/10
A prvalue of type “pointer to member of X of type T1” can be explicitly converted to a prvalue of a different type “pointer to member of Y of type T2” if T1 and T2 are both function types or both object types. The null member pointer value is converted to the null member pointer value of the destination type. The result of this conversion is unspecified, except in the following cases:
Converting a prvalue of type “pointer to member function” to a different pointer-to-member-function type and back to its original type yields the original pointer-to-member value.
Converting a prvalue of type “pointer to data member of X of type T1” to the type “pointer to data member of Y of type T2” (where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer-to-member value.
&a<b>::va<int> et al. are prvalue of type "pointer to member of a<b> of type void(int)" and converting it (without calling the resulting function pointer whose value is unspecified) is legal.
Let's simplify to this example:
struct a {
template <typename... Args>
void va(Args...) {}
template <typename X>
void x(X) {}
void y(int) {}
};
using no_args = void(a::*)();
using int_arg = void(a::*)(int);
And lets try the following four things:
reinterpret_cast<no_args>(&MEMBER_FUNCTION); // (1)
(no_args) &MEMBER_FUNCTION; // (2)
(no_args) static_cast<int_arg>(&MEMBER_FUNCTION); // (3)
int_arg temp = &MEMBER_FUNCTION; (no_args) temp; // (4)
(Replacing MEMBER_FUNCTION with &a::va<int>, &a::x<int> and &a::y).
clang compiles all of them.
gcc compiles everything except (2) with &a::va<int> and &a::x<int>.
MSVC compiles everything except (1) and (2) with &a::va<int> (but is fine with &a::x<int>).
Notice that (3) is essentially the same as (4).
https://gcc.godbolt.org/z/a2qqyo showing an example of this.
What you can see from this is that &MEMBER_FUNCTION is not resolved to a specific member function pointer in case of templates, but if it is resolved, it is allowed to reinterpret it into another member function pointer type.
What the standard has to say:
[over.over]/1:
A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or a pointer to member function for a specific function from the overload set.
A function template name is considered to name a set of overloaded functions in such contexts.
A function with type F is selected for the function type FT of the target type required in the context if F (after possibly applying the function pointer conversion) is identical to FT.
[ Note: That is, the class of which the function is a member is ignored when matching a pointer-to-member-function type.
— end note
]
The target can be:
[...]
- an explicit type conversion ([expr.type.conv], [expr.static.cast], [expr.cast])
An example given later on is:
int f(double);
int f(int);
void g() {
(int (*)(int))&f; // cast expression as selector
}
And a few more quotes about templates:
[temp.deduct.funcaddr]/1:
Template arguments can be deduced from the type specified when taking the address of an overloaded function.
The function template's function type and the specified type are used as the types of P and A, and the deduction is done as described in [temp.deduct.type].
[temp.arg.explicit]/4
[...] if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template specialization.
This seems like MSVC is right.
&a::va<int> is not resolved unless you assign/cast it to a void(a::*)(int). You should also be able to assign it to void(a::*)(int, char) or void(a::*)(int, double, char), where the Args would be deduced as { int, char } and { int, double, char } respectively. That means that (no_args) &a::va<int> should fail, as there are many possible sets of Args it could be (All of them start with int, and clang overzealously resolves it), and none of them take zero parameters, so (no_args) &a::va<int> is a static_cast that should fail.
As for &a::x<int>, there is only one possible function, so it should work exactly the same way as &a::y (But gcc still hasn't resolved it yet).
Related
I am trying to understand why substitution fails on the following snippet unless brackets are added:
template<typename T>
struct A {};
template<typename T>
struct B {
B(A<T>);
};
template<typename T>
void example(A<T>, B<T>);
struct C {};
struct D {
D(C);
};
void example2(C, D);
int main(int argc, char *argv[]) {
example(A<int>{}, A<int>{}); // error
example(A<int>{}, {A<int>{}}); // ok
example2(C{}, C{}); // ok
example2(C{}, {C{}}); // ok
return 0;
}
See this example: https://godbolt.org/z/XPqHww
For example2 I am able to implicitly pass the C{} to the constructor of D without any error. For example I am not allowed to implicitly pass the A<int>{} until I add brackets.
What defines this behaviour?
example is a function template and the function parameters depend on the template parameter. Therefore and since you did not specify any template arguments explicitly in the call example(A<int>{}, A<int>{});, when you call this function, template argument deduction is performed to figure out what type T should be for the call.
With a few exceptions template argument deduction requires that a T can be found such that the type of the argument in the function call matches the type in the function parameter exactly.
The issue with your call is that A<int> does clearly not match B<T> exactly for any T (and none of the exceptions apply either), so the call will fail.
This behavior is necessary, because the compiler would otherwise need to test all possible types T to check whether the function can be called. That would be computationally infeasible or impossible.
In the call example2(C{}, C{}); no templates are involved, so no template argument deduction is performed. Because the compiler doesn't need to figure out the target type in the parameter anymore, it becomes feasible to consider implicit conversions from the known argument type to the known parameter type. One such implicit conversion is the construction of D from C via the non-explicit constructor D(C);. So the call succeeds with that conversion.
example2(C{}, {C{}}); does effectively the same.
The question then is why example(A<int>{}, {A<int>{}}); works. This is because of a specific rule that can be found e.g. in [temp.deduct.type]/5.6 of the C++17 standard (draft N4659). It says that a function argument/parameter pair for which the argument is an initializer list (i.e. {A<int>{}}) and the parameter is not a specialization of std::initializer_list or an array type (it is neither here), the function parameter is a non-deduced context.
Non-deduced context means that the function argument/parameter pair will not be used during template argument deduction to figure out the type of T. This means its type does not need to match exactly. Instead, if template argument deduction otherwise succeeds, the resulting T will simply be substituted into the non-deduced context and from there implicit conversions will be considered as before. B<T> can be constructed from {A<int>} if T = int because of the non-explicit constructor B(A<T>);.
Now the question is whether template argument deduction will succeed and deduce T = int. It can only succeed if a T can be deduced from another function parameter. And indeed there is still the first parameter, for which the types match exactly: A<int>/A<T> matches for T = int and because this function argument doesn't use an initializer list, it is a context from which T will be deduced.
So indeed for example(A<int>{}, {A<int>{}}); deduction from the first argument will yield T = int and substitution into the second parameter B<T> makes the initialization/conversion B<T>{A<int>{}} succeed, so that the call is viable.
If you were to use initializer lists for both parameters as in example({A<int>{}}, {A<int>{}});, both argument/parameter pairs become non-deduced context and there wouldn't be anything left to deduce T from and so the call would fail for failure to deduce T.
You can make all calls work by specifying T explicitly, so that template argument deduction becomes unnecessary, e.g.:
example<int>(A<int>{}, A<int>{});
If P is a class and P has the form simple-template-id, then the transformed A can be a derived class D of the deduced A.
from [temp.deduct.call]4.3
This sentence describes how a function template argument is still valid if it is derived from "deduced A", However, there is not solid definition for what "deduced A" actually is.
My theory is that deduced A is original P with template arguments from A substituted in, but this would break the rules of type deduction trying to find template arguments to make A and deduced A identical, as there would be cases with A being a non-reference and deduced A being a reference.
The goal of function template argument deduction is to figure out which particular specialization of a function template should be used in places where the template name is used like a function name. For example, given a function template
template <typename T>
void f(T* value) {}
when you then have a function call like
int* a = &x;
f(a);
the name f here is not actually the name of a function but the name of a function template. The compiler has to figure out which concrete specialization of the function template this call should actually be calling based on the types of the arguments given in the function call. In other words, it has to figure out which template argument X should be used for the template parameter T to get to an actual function f<X> that could be called here like that. This is a bit of an inverse problem compared to a normal function call. Rather than having to make a list of arguments fit a given signature (by applying conversions), we're now having to make a signature fit a given list of arguments. Another way of looking at it is as trying to deduce template arguments that will make the type of each function parameter match the type of each function call argument. This is what [temp.deduct.call]/4 is talking about here:
In general, the deduction process attempts to find template argument values that will make the deduced A identical to A
Taking our example above, given some deduced template argument X, the deduced argument type is what we get by substituting our deduced X for T into our function parameter type T* (i.e., the type of argument this function parameter takes). If we deduce X to be int, substituting int for T into T* makes our deduced argument type come out to be int*. Since the deduced argument type int* is identical to the type of the actual argument, we've found that the function f<int> is what we were looking for.
To make all of this consistent with how normal function calls behave, there are a few corner cases to take care of. In particular with function call arguments of array and function types, where we normally have array-to-pointer and function-to-pointer decay, as well as top-level const. To deal with this, the standard specifies that the argument type A we're trying to match is not simply taken to be the type of the corresponding function call argument directly but is first transformed by applying the array-to-pointer, function-to-pointer, etc. conversions. This transformed A is the A we're actually trying to make our deduced argument type match. This is just to explain why the standard talks about a "transformed A" there. It's not really that important to the question at hand. The transformed A is just the function argument type we're actually trying to match.
Now, let's say we have some
template <typename T> class B {};
and some derived class
class D : public B<int> {};
When you then have a function template like
template <typename T>
void f(const B<T>*) {}
and a function call like this
D d;
f(&d);
there is no template argument X you could pick for T that would make the deduced argument type const B<X>* equal to D*. But since D is derived from B<int>, deducing the template argument to be int would nevertheless lead to a function specialization f<int> that could take the call. The whole paragraph [temp.deduct.call]/4.3 and especially the sentence from your question
If P is a class and P has the form simple-template-id, then the transformed A can be a derived class D of the deduced A.
is there to allow exactly this to work…
This is an offshoot of an answer to another SO post.
I have the following working code, with the expected output.
#include <iostream>
template <typename T>
T twice(T in)
{
return 2*in;
}
struct Foo
{
Foo operator+(int (*func)(int in)) const
{
Foo ret{data};
ret.data += func(ret.data);
return ret;
}
int data;
};
int main()
{
Foo f1{20};
Foo f2 = f1 + twice;
Foo f3 = f1 + twice<int>;
std::cout << f2.data << std::endl;
std::cout << f3.data << std::endl;
}
I did not know until yesterday that the compiler can deduce the type parameters of a function template even without an argument. In the code above, the expressions
f1 + twice
and
f1 + twice<int>
result in identical values.
My question is: Where in the C++03/C++11 standard can we find the necessary supporting documentation for the compiler's auto type detection logic?
C++11 14.8.2.2 Template arguments can be deduced from the type specified when taking the address of an overloaded function.
Here, the type specified by the parameter type of operator+ is int (*)(int), the overloaded function is twice, so int is deduced as the template argument to give a matching function type. See 14.8.2.5 if you need the gory details of that deduction.
This is closest to the actual c++11 standard but still openly available version of draft I've found: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf.
I believe the template parameter deduction is done when you write Foo f2 = f1 + twice; and pass twice as a function address. twice gets passed as an address of a function to the operator+. I believe the following mechanism kicks in:
14.8.2.2 Deducing template arguments taking the address of a function template
[temp.deduct.funcaddr]:
Template arguments can be deduced from the type specified when taking the address of an overloaded function (13.4). The function template’s function type and the specified type are used as the types of P and A, and the deduction is done as described in 14.8.2.5.
So the actual template deduction will be done basing on the type of the funct. I believe the relevant paragraphs from 14.8.2.5 are 1 and 2.
14.8.2.5 Deducing template arguments from a type [temp.deduct.type]`:
1 Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
2 In some cases, the deduction is done using a single set of types P and A, in other cases, there will be a set of corresponding types P and A. Type deduction is done independently for each P/A pair, and the deduced template argument values are then combined. If type deduction cannot be done for any P/A pair, or if for any pair the deduction leads to more than one possible set of deduced values, or if different pairs yield different deduced values, or if any template argument remains neither deduced nor explicitly specified, template
argument deduction fails.
Basically you pass twice as a pointer to the operator+, and then template arguments get deduced via type of the function as defined in operator+. So you have an actual type A that is int (*)(int in) and template types P of twice that gets matched against A and only int twice(int) fits.
I hope I got everything right.
Say you have two structures, Generic_A and Generic_B. Generic_B is derived from Generic_A. Why is it that when Generic_B tries to access a method in its parent, Generic_A, it generates the following error:
test2.cpp: In function 'int main()':
test2.cpp:26: error: no matching function for call to 'case1(void (Generic_A::*)()'
This code, compiled using gcc version 4.4.6, replicates the problem:
#include <stdio.h>
struct Generic_A
{
void p1() { printf("%s\n", __PRETTY_FUNCTION__); };
};
struct Generic_B : public Generic_A
{
void p2() { printf("%s\n", __PRETTY_FUNCTION__); };
};
template <class T,class... ARGS>
void case1( void (T::*p)(ARGS...) ) {
printf("%s\n", __PRETTY_FUNCTION__);
}
template <class T>
void case2( void (T::*p)() ) {
printf("%s\n", __PRETTY_FUNCTION__);
}
main()
{
//generates error
case1<Generic_B>(&Generic_B::p1);
//compiles fine
case2<Generic_B>(&Generic_B::p1);
}
The only apparent difference between the two function calls is that case1() has a template argument parameter, and case2() doesn't. Shouldn't they both allow you to pass a function pointer to a method in Generic_B's parent (ie &Generic_B::p1)?
Also, casting the function pointer in case1 seems to sometimes resolve the error:
case1<Generic_B>( (void (Generic_B::*)()) &Generic_B::p1);
What is going on?
This is tricky, but it turns out g++ is correct.
First, the type of expression &Generic_B::p1 is void (Generic_A::*)(). The compiler uses Generic_B:: to qualify its name lookup and finds the member of Generic_A. The expression type depends on the definition of the member found, not the type used within the qualified-id.
But it's also legal to have
void (Generic_B::*member)() = &Generic_B::p1;
since there is an implicit conversion from void (Generic_A::*)() to void (Generic_B::*)().
Whenever a function template is used as a function call, the compiler goes through three basic steps (or attempts to):
Substitute any explicit template arguments for the template parameters in the function declaration.
For each function parameter that still involves at least one template parameter, compare the corresponding function argument to that function parameter, to (possibly) deduce those template parameters.
Substitute the deduced template parameters into the function declaration.
In this case, we have the function template declaration
template <class T,class... ARGS>
void case1( void (T::*p)(ARGS...) );
where the template parameters are T and ARGS, and the function call expression
case1<Generic_B>(&Generic_B::p1)
where the explicit template argument is Generic_B and the function argument is &Generic_B::p1.
So step 1, substitute the explicit template argument:
void case1( void (Generic_B::*p)(ARGS...) );
Step 2, compare parameter types and argument types:
The parameter type (P in Standard section 14.8.2) is void (Generic_B::*)(ARGS...). The argument type (A) is void (Generic_A::*)().
C++ Standard (N3485) 14.8.2.1p4:
In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above). However, there are three cases that allow a difference:
If the original P is a reference type, the deduced A (i.e., the type referred to by the reference) can be more cv-qualified than the transformed A.
The transformed A can be another pointer or pointer to member type that can be converted to the deduced A via a qualification conversion (4.4).
If P is a class and P has the form simple-template-id, then the transformed A can be a derived class of the deduced A. Likewise, if P is a pointer to a class of the form simple-template-id, the transformed A can be a pointer to a derived class pointed to by the deduced A.
So type deduction allows for certain implicit conversions involving const / volatile and/or derived-to-base conversions, but implicit conversions of pointers to members are not considered.
In the case1 example, type deduction fails, and the function is not a match.
Unfortunately, there's no way to explicitly specify that your template parameter pack ARGS should be substituted with an empty list. As you already discovered, you can get this working by explicitly doing the necessary pointer to member function conversion yourself, even though it's otherwise valid as an implicit conversion.
Yay, another question title composed of a random sequence of C++ terms!
Usually we make a class Callable by implementing operator(). But you can also do so by implementing a user-defined conversion to function pointer or reference type. Instead of using perfect forwarding, a conversion function can return a pointer to a function which is then called with the original argument list.
struct call_printf {
typedef int printf_t( char const *, ... );
operator printf_t & () { return std::printf; }
};
http://ideone.com/kqrJz
As far as I can tell, the typedef above is a syntactic necessity. The name of a conversion function is formed from a type-specifier-seq, which does not allow a construct like int (*)(). That would require an abstract-declarator. Presumably the reason is that such type names get complicated, and complex constructs used as object names are tough to parse.
Conversion functions are also allowed to be templated, but the template arguments must be deduced, because there is nowhere to explicitly specify them. (That would defeat the whole point of implicit conversion.)
Question #1: In C++03, is there was no way to specify a function conversion operator template? It appears there was no way to resolve the template arguments (i.e., name them in a deduced context) in an acceptable function pointer type.
Here is the equivalent reference from C++11, §13.3.1.1.2/2 [over.call.object]. It's substantially the same from C++03:
In addition, for each non-explicit conversion function declared in T of the form
operator conversion-type-id () cv-qualifier attribute-specifier-seqopt;
where cv-qualifier is the same cv-qualification as, or a greater cv-qualification than, cv, and where conversion-type-id denotes the type “pointer to function of (P1,...,Pn) returning R”, or the type “reference to pointer to function of (P1,...,Pn) returning R”, or the type “reference to function of (P1,...,Pn) returning R”, a surrogate call function with the unique name call-function and having the form
R call-function ( conversion-type-id F, P1 a1, ... ,Pn an) { return F (a1,... ,an); }
is also considered as a candidate function. Similarly, surrogate call functions are added to the set of candidate functions for each non-explicit conversion function declared in a base class of T provided the
function is not hidden within T by another intervening declaration.
Question #2: In C++11, can such a conversion be specified using a default template argument? This is useful for SFINAE. The only difference here from the above example is that the conversion-type-id only represents a function reference after instantiation, because it's a dependent type (despite invariance). This trips up GCC and it skips the member template.
enum { call_alternate = true; }
struct call_switch {
template< bool en = call_alternate >
operator typename std::enable_if< en, decltype( fn_1 ) & >::type ()
{ return fn_1; }
template< bool en = ! call_alternate >
operator typename std::enable_if< en, decltype( fn_2 ) & >::type ()
{ return fn_2; }
};
We also have alias templates. It seems that alias substitution occurs before instantiation, given the example in §14.5.7/2, where the declarations of process conflict. In GCC 4.7, this code at least instantiates the declaration, but then it produces a bizarre "candidate expects 2 arguments, 2 provided" error.
template< typename t >
using fn_t = void (&)( t );
struct talk {
template< typename t >
operator fn_t< t >() { return fn; }
};
int main() {
talk()( 3 );
}
Question #1: In C++03, is there was no way to specify a function conversion operator template? It appears there was no way to resolve the template arguments (i.e., name them in a deduced context) in an acceptable function pointer type.
Yes, that is correct.
Question #2: In C++11, can such a conversion be specified using a default template argument?
It can, and you can also use alias templates, but you cannot use such a conversion function template for creating surrogate call functions. You can use it for converting your class object to function pointers in implicit conversions otherwise.
We also have alias templates. It seems that alias substitution occurs before instantiation, given the example in §14.5.7/2, where the declarations of process conflict. In GCC 4.7, this code at least instantiates the declaration, but then it produces a bizarre "candidate expects 2 arguments, 2 provided" error.
Yes, this is https://groups.google.com/forum/?fromgroups#!topic/comp.std.c++/lXLFBcF_m3c (and caused closure of DR395), but even though such a conversion function template can work in cases like void(&p)() = yourClassObject, it won't work for surrogate call functions, because there the conversion function needs to provide a fixed non-dependent type that the class object is converted to when the surrogate function is called, but a conversion function template does not provide such a type normally (weird things like template<typename = int> operator Identity<void(*)()>(); aside...).
I think that GCC may incorrectly generates the candidate call-function(void (&)( t ), t) with the dependent types still there and try to call that candidate, thereby violating some invariant of it (which could explain the weird error message - possibly hitting an } else { ... } unexpectedly somewhere).