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) );
}
Related
I was surprised the following code resulted in a could not deduce template argument for T error:
struct foo
{
template <typename T>
void bar(int a, T b = 0.0f)
{
}
};
int main()
{
foo a;
a.bar(5);
return 0;
}
Calling a.bar<float>(5) fixes the issue. Why can't the compiler deduce the type from the default argument?
In C++03, the specification explicitly prohibits the default argument from being used to deduce a template argument (C++03 §14.8.2/17):
A template type-parameter cannot be deduced from the type of a function default argument.
In C++11, you can provide a default template argument for the function template:
template <typename T = float>
void bar(int a, T b = 0.0f) { }
The default template argument is required, though. If the default template argument is not provided, the default function argument is still not usable for template argument deduction. Specifically, the following applies (C++11 14.8.2.5/5):
The non-deduced contexts are:
...
A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.
There would be some technical difficulties in achieving that in general. Remember that default arguments in templates are not instantiated until they are needed. Consider then:
template<typename T, typename U> void f(U p = T::g()); // (A)
template<typename T> T f(long, int = T()); // (B)
int r = f<int>(1);
This is resolved today by performing (among other things) the following steps:
attempt to deduce template parameters for candidates (A) and (B);
this fails for (A), which is therefore eliminated.
perform overload resolution; (B) is selected
form the call, instantiating the default argument
In order to deduce from a default argument, that default argument would have to be itself instantiated before completing the deduction process. That could fail, leading to errors outside the SFINAE context. I.e., a candidate that may be entirely inappropriate for a call could trigger an error.
A good reason might be that
void foo(bar, xyzzy = 0);
is similar to a pair of overloads.
void foo(bar b) { foo(b, 0); }
foo(bar, xyzzy);
Moreover, sometimes it is advantageous to refactor it into such:
void foo(bar b) { /* something other than foo(b, 0); */ }
foo(bar, xyzzy);
Even when written as one, it's still like two functions in one, neither of which is "preferred" in any sense. You're calling the one-argument function; the two-argument one is effectively a different function. The default argument notation just merges them into one.
If overloading were to have the behavior that you are asking for, then for consistency it would have to work in the case when the template is split up into two definitions. That wouldn't make sense because then the deduction would be pulling types from an unrelated function that is not being called! And if it was not implemented, it would mean that overloading different parameter list lengths becomes a "second class citizen" compared to "default-argumenting".
It is good if the difference between overloads and defaulting is completely hidden to the client.
I was surprised the following code resulted in a could not deduce template argument for T error:
struct foo
{
template <typename T>
void bar(int a, T b = 0.0f)
{
}
};
int main()
{
foo a;
a.bar(5);
return 0;
}
Calling a.bar<float>(5) fixes the issue. Why can't the compiler deduce the type from the default argument?
In C++03, the specification explicitly prohibits the default argument from being used to deduce a template argument (C++03 §14.8.2/17):
A template type-parameter cannot be deduced from the type of a function default argument.
In C++11, you can provide a default template argument for the function template:
template <typename T = float>
void bar(int a, T b = 0.0f) { }
The default template argument is required, though. If the default template argument is not provided, the default function argument is still not usable for template argument deduction. Specifically, the following applies (C++11 14.8.2.5/5):
The non-deduced contexts are:
...
A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.
There would be some technical difficulties in achieving that in general. Remember that default arguments in templates are not instantiated until they are needed. Consider then:
template<typename T, typename U> void f(U p = T::g()); // (A)
template<typename T> T f(long, int = T()); // (B)
int r = f<int>(1);
This is resolved today by performing (among other things) the following steps:
attempt to deduce template parameters for candidates (A) and (B);
this fails for (A), which is therefore eliminated.
perform overload resolution; (B) is selected
form the call, instantiating the default argument
In order to deduce from a default argument, that default argument would have to be itself instantiated before completing the deduction process. That could fail, leading to errors outside the SFINAE context. I.e., a candidate that may be entirely inappropriate for a call could trigger an error.
A good reason might be that
void foo(bar, xyzzy = 0);
is similar to a pair of overloads.
void foo(bar b) { foo(b, 0); }
foo(bar, xyzzy);
Moreover, sometimes it is advantageous to refactor it into such:
void foo(bar b) { /* something other than foo(b, 0); */ }
foo(bar, xyzzy);
Even when written as one, it's still like two functions in one, neither of which is "preferred" in any sense. You're calling the one-argument function; the two-argument one is effectively a different function. The default argument notation just merges them into one.
If overloading were to have the behavior that you are asking for, then for consistency it would have to work in the case when the template is split up into two definitions. That wouldn't make sense because then the deduction would be pulling types from an unrelated function that is not being called! And if it was not implemented, it would mean that overloading different parameter list lengths becomes a "second class citizen" compared to "default-argumenting".
It is good if the difference between overloads and defaulting is completely hidden to the client.
Consider the following MCVE
struct A {};
template<class T>
void test(T, T) {
}
template<class T>
class Wrapper {
using type = typename T::type;
};
template<class T>
void test(Wrapper<T>, Wrapper<T>) {
}
int main() {
A a, b;
test(a, b); // works
test<A>(a, b); // doesn't work
return 0;
}
Here test(a, b); works and test<A>(a, b); fails with:
<source>:11:30: error: no type named 'type' in 'A'
using type = typename T::type;
~~~~~~~~~~~~^~~~
<source>:23:13: note: in instantiation of template class 'Wrap<A>' requested here
test<A>(a, b); // doesn't work
^
<source>:23:5: note: while substituting deduced template arguments into function template 'test' [with T = A]
test<A>(a, b); // doesn't work
LIVE DEMO
Question: Why is that? Shouldn't SFINAE work during substitution? Yet here it seems to work during deduction only.
Self introduction
Hello everyone, I am an innocent compiler.
The first call
test(a, b); // works
In this call, the argument type is A. Let me first consider the first overload:
template <class T>
void test(T, T);
Easy. T = A.
Now consider the second:
template <class T>
void test(Wrapper<T>, Wrapper<T>);
Hmm ... what? Wrapper<T> for A? I have to instantiate Wrapper<T> for every possible type T in the world just to make sure that a parameter of type Wrapper<T>, which might be specialized, can't be initialized with an argument of type A? Well ... I don't think I'm going to do that ...
Hence I will not instantiate any Wrapper<T>. I will choose the first overload.
The second call
test<A>(a, b); // doesn't work
test<A>? Aha, I don't have to do deduction. Let me just check the two overloads.
template <class T>
void test(T, T);
T = A. Now substitute — the signature is (A, A). Perfect.
template <class T>
void test(Wrapper<T>, Wrapper<T>);
T = A. Now subst ... Wait, I never instantiated Wrapper<A>? I can't substitute then. How can I know whether this would be a viable overload for the call? Well, I have to instantiate it first. (instantiating) Wait ...
using type = typename T::type;
A::type? Error!
Back to L. F.
Hello everyone, I am L. F. Let's review what the compiler has done.
Was the compiler innocent enough? Did he (she?) conform to the standard?
#YSC has pointed out that [temp.over]/1 says:
When a call to the name of a function or function template is written
(explicitly, or implicitly using the operator notation), template
argument deduction ([temp.deduct]) and checking of any explicit
template arguments ([temp.arg]) 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. For each
function template, if the argument deduction and checking succeeds,
the template-arguments (deduced and/or explicit) are used to
synthesize the declaration of 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 or the synthesized function template
specialization would be ill-formed, no such function is added to the
set of candidate functions for that template. The complete set of
candidate functions includes all the synthesized declarations and all
of the non-template overloaded functions of the same name. The
synthesized declarations are treated like any other functions in the
remainder of overload resolution, except as explicitly noted in
[over.match.best].
The missing type leads to a hard error. Read https://stackoverflow.com/a/15261234. Basically, we have two stages when determining whether template<class T> void test(Wrapper<T>, Wrapper<T>) is the desired overload:
Instantiation. In this case, we (fully) instantiate Wrapper<A>. In this stage, using type = typename T::type; is problematic because A::type is nonexistent. Problems that occur in this stage are hard errors.
Substitution. Since the first stage already fails, this stage is not even reached in this case. Problems that occur in this stage are subject to SFINAE.
So yeah, the innocent compiler has done the right thing.
I'm not a language lawyer but I don't think that defining a using type = typename T::type; inside a class is, itself, usable as SFINAE to enable/disable a function receiving an object of that class.
If you want a solution, you can apply SFINAE to the Wrapper version as follows
template<class T>
auto test(Wrapper<T>, Wrapper<T>)
-> decltype( T::type, void() )
{ }
This way, this test() function is enabled only for T types with a type type defined inside it.
In your version, is enabled for every T type but gives error when T is incompatible with Wrapper.
-- EDIT --
The OP precises and asks
My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper itself can be instantiated?
As suggested by Holt, you can create a custom type traits to see if a type is a Wrapper<something> type; by example
template <typename>
struct is_wrapper : public std::false_type
{ };
template <typename T>
struct is_wrapper<Wrapper<T>> : public std::true_type
{ using type = T; };
Then you can modify the Wrapper version to receive a U type and check if U is a Wrapper<something> type
template <typename U>
std::enable_if_t<is_wrapper<U>{}> test (U, U)
{ using T = typename is_wrapper<U>::type; }
Observe that you can recover the original T type (if you need it) using the type definition inside the is_wrapper struct.
If you need a non-Wrapper version of test(), with this solution you have to explicity disable it when T is a Wrapper<something> type to avoid collision
template <typename T>
std::enable_if_t<!is_wrapper<T>{}> test(T, T)
{ }
Deduction of the function called in a function call expression is performed in two steps:
Determination of the set of viable functions;
Determination of the best viable function.
The set of viable function can only contain function declaration and template function specialization declaration.
So when a call expression (test(a,b) or test<A>(a,b)) names a template function, it is necessary to determine all template arguments: this is called template argument deduction. This is performed in three steps [temp.deduct]:
Subsitution of explicitly provided template arguments (in names<A>(x,y) A is explicitly provided);(substitution means that in the function template delcaration, the template parameter are replaced by their argument)
Deduction of template arguments that are not provided;
Substitution of deduced template argument.
Call expression test(a,b)
There are no explictly provided template argument.
T is deduced to A for the first template function, deduction fails for the second template function [temp.deduct.type]/8. So the second template function will not participate to overload resolution
A is subsituted in the declaration of the first template function. The subsitution succeeds.
So there is only one overload in the set and it is selected by overload resolution.
Call expression test<A>(a,b)
(Edit after the pertinent remarks of #T.C. and #geza)
The template argument is provided: A and it is substituted in the declaration of the two template functions. This substitution only involves the instantiation of the declaration of the function template specialization. So it is fine for the two template
No deduction of template argument
No substitution of deduced template argument.
So the two template specializations, test<A>(A,A) and test<A>(Wrapper<A>,Wrapper<A>), participate in overload resolution. First the compiler must determine which function are viable. To do that the compiler needs to find an implicit conversion sequence that converts the function argument to the function parameter type [over.match.viable]/4:
Third, for F to be a viable function, there shall exist for each argument an implicit conversion sequence that converts that argument to the corresponding parameter of F.
For the second overload, in order to find a conversion to Wrapper<A> the compiler needs the definition of this class. So it (implicitly) instantiates it. This is this instantiation that causes the observed error generated by compilers.
I was surprised the following code resulted in a could not deduce template argument for T error:
struct foo
{
template <typename T>
void bar(int a, T b = 0.0f)
{
}
};
int main()
{
foo a;
a.bar(5);
return 0;
}
Calling a.bar<float>(5) fixes the issue. Why can't the compiler deduce the type from the default argument?
In C++03, the specification explicitly prohibits the default argument from being used to deduce a template argument (C++03 §14.8.2/17):
A template type-parameter cannot be deduced from the type of a function default argument.
In C++11, you can provide a default template argument for the function template:
template <typename T = float>
void bar(int a, T b = 0.0f) { }
The default template argument is required, though. If the default template argument is not provided, the default function argument is still not usable for template argument deduction. Specifically, the following applies (C++11 14.8.2.5/5):
The non-deduced contexts are:
...
A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.
There would be some technical difficulties in achieving that in general. Remember that default arguments in templates are not instantiated until they are needed. Consider then:
template<typename T, typename U> void f(U p = T::g()); // (A)
template<typename T> T f(long, int = T()); // (B)
int r = f<int>(1);
This is resolved today by performing (among other things) the following steps:
attempt to deduce template parameters for candidates (A) and (B);
this fails for (A), which is therefore eliminated.
perform overload resolution; (B) is selected
form the call, instantiating the default argument
In order to deduce from a default argument, that default argument would have to be itself instantiated before completing the deduction process. That could fail, leading to errors outside the SFINAE context. I.e., a candidate that may be entirely inappropriate for a call could trigger an error.
A good reason might be that
void foo(bar, xyzzy = 0);
is similar to a pair of overloads.
void foo(bar b) { foo(b, 0); }
foo(bar, xyzzy);
Moreover, sometimes it is advantageous to refactor it into such:
void foo(bar b) { /* something other than foo(b, 0); */ }
foo(bar, xyzzy);
Even when written as one, it's still like two functions in one, neither of which is "preferred" in any sense. You're calling the one-argument function; the two-argument one is effectively a different function. The default argument notation just merges them into one.
If overloading were to have the behavior that you are asking for, then for consistency it would have to work in the case when the template is split up into two definitions. That wouldn't make sense because then the deduction would be pulling types from an unrelated function that is not being called! And if it was not implemented, it would mean that overloading different parameter list lengths becomes a "second class citizen" compared to "default-argumenting".
It is good if the difference between overloads and defaulting is completely hidden to the client.
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.