Why can template instances not be deduced in `std::reference_wrapper`s? - c++

Suppose I have some object of type T, and I want to put it into a reference wrapper:
int a = 5, b = 7;
std::reference_wrapper<int> p(a), q(b); // or "auto p = std::ref(a)"
Now I can readily say if (p < q), because the reference wrapper has a conversion to its wrapped type. All is happy, and I can process a collection of reference wrappers just like they were the original objects.
(As the question linked below shows, this can be a useful way to produce an alternate view of an existing collection, which can be rearranged at will without incurring the cost of a full copy, as well as maintaining update integrity with the original collection.)
However, with some classes this doesn't work:
std::string s1 = "hello", s2 = "world";
std::reference_wrapper<std::string> t1(s1), t2(s2);
return t1 < t2; // ERROR
My workaround is to define a predicate as in this answer*; but my question is:
Why and when can operators be applied to reference wrappers and transparently use the operators of the wrapped types? Why does it fail for std::string? What has it got to do with the fact that std::string is a template instance?
*) Update: In the light of the answers, it seems that using std::less<T>() is a general solution.

Edit: Moved my guesswork to the bottom, here comes the normative text why this won't work. TL;DR version:
No conversions allowed if the function parameter contains a deduced template parameter.
§14.8.3 [temp.over] p1
[...] When a call to that name is written (explicitly, or implicitly using the operator
notation), template argument deduction (14.8.2) and checking of any explicit template arguments (14.3) are performed for each function template to find the template argument values (if any) that can be used with that function template to instantiate a function template specialization that can be invoked with the call arguments.
§14.8.2.1 [temp.deduct.call] p4
[...] [ Note: as specified in 14.8.1, implicit conversions will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter contains no template-parameters that participate in template argument deduction. [...] —end note ]
§14.8.1 [temp.arg.explicit] p6
Implicit conversions (Clause 4) will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter type contains no template-parameters that participate in template argument deduction. [ Note: Template parameters do not participate in template argument deduction if they are explicitly specified. [...] —end note ]
Since std::basic_string depends on deduced template parameters (CharT, Traits), no conversions are allowed.
This is kind of a chicken and egg problem. To deduce the template argument, it needs an actual instance of std::basic_string. To convert to the wrapped type, a conversion target is needed. That target has to be an actual type, which a class template is not. The compiler would have to test all possible instantiations of std::basic_string against the conversion operator or something like that, which is impossible.
Suppose the following minimal testcase:
#include <functional>
template<class T>
struct foo{
int value;
};
template<class T>
bool operator<(foo<T> const& lhs, foo<T> const& rhs){
return lhs.value < rhs.value;
}
// comment this out to get a deduction failure
bool operator<(foo<int> const& lhs, foo<int> const& rhs){
return lhs.value < rhs.value;
}
int main(){
foo<int> f1 = { 1 }, f2 = { 2 };
auto ref1 = std::ref(f1), ref2 = std::ref(f2);
ref1 < ref2;
}
If we don't provide the overload for an instantiation on int, the deduction fails. If we provide that overload, it's something the compiler can test against with the one allowed user-defined conversion (foo<int> const& being the conversion target). Since the conversion matches in this case, overload resolution succeeds and we got our function call.

std::reference_wrapper does not have an operator<, so the only way to do ref_wrapper<ref_wrapper is via the ref_wrapper member:
operator T& () const noexcept;
As you know, std::string is:
typedef basic_string<char> string;
The relevant declaration for string<string is:
template<class charT, class traits, class Allocator>
bool operator< (const basic_string<charT,traits,Allocator>& lhs,
const basic_string<charT,traits,Allocator>& rhs) noexcept;
For string<string this function declaration template is instantiated by matching string = basic_string<charT,traits,Allocator> which resolves to charT = char, etc.
Because std::reference_wrapper (or any of its (zero) bases classes) cannot match basic_string<charT,traits,Allocator>, the function declaration template cannot be instantiated into a function declaration, and cannot participate in overloading.
What matters here is that there is no non-template operator< (string, string) prototype.
Minimal code showing the problem
template <typename T>
class Parametrized {};
template <typename T>
void f (Parametrized<T>);
Parametrized<int> p_i;
class Convertible {
public:
operator Parametrized<int> ();
};
Convertible c;
int main() {
f (p_i); // deduce template parameter (T = int)
f (c); // error: cannot instantiate template
}
Gives:
In function 'int main()':
Line 18: error: no matching function for call to 'f(Convertible&)'
Standard citations
14.8.2.1 Deducing template arguments from a function call [temp.deduct.call]
Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below.
(...)
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.
Note that this is the case with std::string()<std::string().
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).
See comment below.
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.
Comment
This implies that in this paragraph:
14.8.1 Explicit template argument specification [temp.arg.explicit]/6
Implicit conversions (Clause 4) will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter type contains no template-parameters that participate in template argument deduction.
the if should not be taken as a if and only if, as it would directly contradict the text quoted previously.

Related

Could not deduce template argument (vector, std::function)

I created a templated function of which I am trying to automatically deduce the template argument.
MCVE(compile it):
template<class Value, class Allocator>
void foo(const std::vector<Value, Allocator>& v, const std::function<void(const Value&)>& f)
{
}
int main()
{
vector<int> v;
foo<int>(v, [](const int&){}); //okay
//foo(v, [](const int&){}); //not okay
return 0;
}
I first thought the Allocator could not be deduced but that does not seem to solve it.
My next guess is it has something to do with the lambda to std::function but no idea on further steps there. Anybody got any clues about what I need to do to make this deducible?
Ps: I know "const int&" could become "int" but in the real code there is a non scalar data type there.
Template argument deduction happens before implicit conversion of Lambda to std::fucntion.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
Meaning type deduction of the template parameter Value on the 2nd function argument f fails because the implicit conversion from lambda to std::function are not considered.
As you showed you can specify template arguments explicitly (to bypass the template argument deduction). Or you can exclude the 2nd argument from deduction by using std::type_identity to declare it as non deduced context.
The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:
e.g.
template<class Value, class Allocator>
void foo(const std::vector<Value, Allocator>& v, const std::function<void(const std::type_identity_t<Value>&)>& f)
{
}
LIVE
PS: std::type_identity is supported from C++20; if the compiler you're using doesn't support it it's not hard to make one.

convert member function to pointer to member function

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).

Auto detection of template parameters without arguments

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.

Templated conversion function to function pointer

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).

Explicit Instantiation

This was motivated by this article (page 5)
template<class T>
T const &f(T const &a, T const &b){
return (a > b ? a : b);
}
template int const &f<int>(int const &, int const &);
int main(){
int x = 0, y = 0;
short s = 0;
f(x, y); // OK
f(x, s); // Is this call well-formed?
}
Is the call 'f(x, s)' well-formed? I assumed that since the function template 'f' is explicitly instantiated, standard conversions would be applied and hence 'short s' would be converted to 'int' to match the call to the explicit specialization 'f<int>'. But it appears that this is ill-formed?
Which part of the Standard talks about the applicable rules in this context?
No, the call f(x, s) is not well-formed. Since you do not explicitly state the specialization to be used, the compiler uses argument deduction to attempt to instantiate the function template; this fails because x and s have different types so T is ambiguous.
The applicable rule is in the specification of the overload resolution process in 13.3.1:
In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction (14.8.3, 14.8.2). Those candidates are then handled as candidate functions in the usual way.
14.8.3/1 is also relevant:
For each function template, if the argument deduction and checking succeeds, the template arguments (deduced and/or explicit) are used to instantiate a single function template specialization which is added to the candidate functions set to be used in overload resolution. If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for that template.
The function template is explicitly instantiated for T = int, but the compiler doesn't know that it should use this instantiation until after it performs template argument deduction to determine what T should be.
The call f(x, s) is syntactically well-formed, but the compiler will not be able to deduce the template parameter T from it because is could a int or a short (because of the first and second arguments). Instantiating the template does not help, that only indicates the compiler to compile that specialization and add it to the generated object file.
If you want the call to cast s to a int automatically, use f<int>(x, s).
An explicitly instantiated specialization doesn't have any higher priority or preferential treatment. It simply exists in its entirety from the point of instantiation. Useful for libraries.
The compiler simply can't figure out which argument to convert, and gets stuck just as it would without the extra declaration.
By the way, if you return it a reference to an argument which was converted, it will be dangling once the temporary expires. If the arguments are references to different types, there is no way to properly form the return value.
Here is my updated min:
#include <type_traits>
template< typename A, typename B >
typename std::common_type< A, B >::type // cannot return ref
my_min( A const &a, B const &b ) {
return a < b? a : b;
}
// enable_if< is_same< A, B > > the old, return-by-reference impl
int main() {
int q = my_min( short(5), long(3) );
}