If we've this function template,
template<typename T>
void f(T param) {}
Then we can call it in the following ways,
int i=0;
f<int>(i);//T=int : no need to deduce T
f(i); //T=int : deduced T from the function argument!
//likewise
sample s;
f(s); //T=sample : deduced T from the function argument!
Now consider this variant of the above function template,
template<typename TArg, typename TBody>
void g(TArg param)
{
TBody v=param.member;
}
Now, can the compiler deduce the template arguments if we write,
sample s;
g(s); //TArg=sample, TBody=int??
Suppose sample is defined as,
struct sample
{
int member;
};
There are basically two questions:
Can the compiler deduce the template arguments in the second example?
If no, then why? Is there any difficulty? If the Standard doesn't say anything about "template argument deduction from function body", then is it because the argument(s) cannot be deduced? Or it didn't consider such deduction so as to avoid adding more complexity to the language? Or what?
I would like know your views on such deduction.
EDIT:
By the way, the GCC is able to deduce function arguments if we write this code:
template<typename T>
void h(T p)
{
cout << "g() " << p << endl;
return;
}
template<typename T>
void g(T p)
{
h(p.member); //if here GCC can deduce T for h(), then why not TBody in the previous example?
return;
}
Working demonstration for this example : http://www.ideone.com/cvXEA
Not working demonstration for the previous example: http://www.ideone.com/UX038
You probably already concluded that the compiler won't deduce TBody by examining the type of sample.member. This would add yet another level of complexity to the template deduction algorithm.
The template matching algorithm only considers function signatures, not their bodies. While not used too often, it's perfectly legal to simply declare a templated function without providing the body:
template <typename T> void f(T param);
This satisfies the compiler. In order to satisfy the linker, you must of course also define the function body somewhere, and ensure that all required instantiations have been provided. But the function body does not necessarily have to be visible to client code of the template function, as long as the required instantiations are available at link time. The body would have to explicitly instantiate the function, eg:
template <> void f(int param);
But this only partially applies to your questions, because you could imagine a scenario like the following, where a 2nd parameter could be deduced from the a provided default parameter, and which won't compile:
template<typename TArg, typename TBody>
void g(TArg param, TBody body = param.member); // won't deduce TBody from TArg
The template matching algorithm considers only the actual type, not any potential nested member types in case of classes or structs. This would have added another level of complexity which apparently was judged to be too complex. Where should the algorithm stop? Are members of members, and so forth, also to be considered?
Also, it's not required because there are other means of achieving the same intention, as shown in the example below.
Nothing prevents you from writing:
struct sample
{
typedef int MemberType;
MemberType member;
};
template<typename TArg>
void g(TArg param)
{
typename TArg::MemberType v = param.member;
}
sample s = { 0 };
g(s);
in order to obtain the same effect.
Regarding your sample you added after editing: whereas it seems that h(p.member) does depend on the member of the struct, and hence the template matching algorithm should fail, it doesn't because you made it a two-step process:
Upon seeing g(s);, the compiler looks for any function taking an argument of type sample (templated or not!). In your case, the best match is void g(T p). At this point, the compiler has not even looked at the body of g(T p) yet!.
Now, the compiler creates a instance of g(T p), specialized for T: sample. So when it sees h(p.member) it knows that p.member is of type int, and will try locate a function h() taking an argument of type int. Your template function h(T p) turns out to be the best match.
Note that if you had written (note the NOT_A_member):
template<typename T>
void g(T p)
{
h(p.NOT_A_member);
return;
}
then the compiler would still consider g() a valid match at stage 1. You then get an error when it turns out that sample does not have a member called NOT_A_member.
There are a couple of things that the compiler cannot possibly do with the code that you provide, the first of which is deduce the second template argument TBody. First, type deduction is only applied to the arguments of the function when the compiler is trying to match the call. At that point the definition of the templated function is not even looked at.
For extra credits, even if the compiler were to look the function definition, the code TBody v = parameter.member is non-deducible in itself, as there are potentially infinite data types that can accept a parameter.member in the constructor.
Now, on the second block of code. To understand it the whole process of template compilation starts when the compiler sees the function call g(x) at that point of call. The compiler sees that the best candidate is the template function template <typename T> void g( T ) and determines what the type T is as part of the overload resolution. Once the compiler determines that it is a call to the template, it performs the first pass of compilation on that function.
During the first pass, syntactic checks are perform without an actual substitution of the type, so the template argument T is still any-type and the argument p is of a yet-unknown type. During the first pass, the code is verified, but dependent names are skipped and their meaning is just assumed. When the compiler sees p.member, and p is of type T that is a template argument it assumes that it will be a member of the yet unknown type (this is the reason why if it is a type you would have to qualify it here with typename). The call h(p.member); is also dependent on the type argument T and is left as it is, assuming that once the type substitution takes place everything will make sense.
Then the compiler does actually substitute the type. At this step T is no longer a generic type, but it represents the concrete type sample. Now the compiler during the second pass tries to fill in the gaps that were left during the first pass. When it sees p.member it looks member inside the type and determines that it is an int and tries to resolve the call h( p.member ); with that knowledge. Because the type T has been resolved before this second stage, this is equivalent to the outside call g(x): all types are known and the compiler only needs to resolve what is the best overload for a function call h that takes an argument of type int&, and the whole process starts again, the template h is found as the best candidate, and ...
It is really important for metaprogramming to understand that type deduction is performed only on the actual signature of the function and not the body, and that is what makes it non-trivial for beginners. Using enable_if (from boost or elsewhere) in the function signature as argument or return type is not a coincidence, but the only way of having the compiler fail to substitute the type before the template is selected as best candidate and the substitution failure is turned into an actual error (and not SFINAE)
No compiler could possibly implement this functionality in a consistent manner. You are simply asking too much.
TBody might be ambiguous, because sample might not be the only type that has a member member. Furthermore, if g calls other template functions, the compiler has no way of knowing what other restrictions might be imposed on TBody.
So in some edge cases, it is theoretically possible to deduce the proper type for TBody, generally it is not.
Related
I am very new to c++ and am trying to understand how "generic" types are "enforced" in templates, specifically with something like charT.
After reading this question I understand that charT can be any char-like object, but I am wondering what the appropriate way is to check that the user actually supplied a valid charT.
In other words, what happens if you provide a double instead? Do you just rely on the complier throwing an error when the function inevitably tries to do something invalid with the double? Or is there a formal way to check that the supplied type is valid?
Generally, this is can be done in C++ using type traits, std::enable_if and SFINAE (Substitution Failure Is Not An Error), assuming that you're using pre-C++20 code. The basic principle is to check if a type has a certain property and if it doesn't, to disable a function overload or class specialization.
For example, if you want to write a function template that only makes sense for classes derived from a certain base class, you can write:
#include <type_traits>
struct Base {};
struct Derived : public Base {};
template<typename T, typename = std::enable_if_t<std::is_base_of_v<Base, T>>>
void foo(const T&) {
}
int main() {
Derived d{};
foo(d);
//foo(1); <-- ERROR: requirement not fulfilled
return 0;
}
Here, a second, unnamed template parameter is used and assigned to the type "returned" by std::enable_if_t (this is shorthand for typename std::enable_if<...>::type to save one from typing out all that and is available as of C++14). std::enable_if will only have the type definition called type if the first parameter evaluates to true. In this example, we check if std::is_base_of_v<Base, T> is true. If that is the case, then std::enable_if will be instantiated to have a using type = void; declaration (if one desires different type than void, this can be passed to std::enable_if as a second parameter right after the bool parameter). Therefore, the unnamed parameter will be set to void and the program compiles. If, however, the first parameter evaluates to false, there will be no type definition in std::enable_if, and the function template will not be instantiated, because the type for the second parameter in our template cannot be substituted, which will merely remove the function from the overload set and not cause a compiler error. This is what SFINAE means. Of course if no other viable overload can be found, you will still get an error, but not due to the fact that the template instantiation failed.
If you uncomment the call to foo(1), clang, for instance, will say:
<source>:15:5: error: no matching function for call to 'foo'
foo(1);
^~~
<source>:8:6: note: candidate template ignored: requirement 'std::is_base_of_v<Base, int>' was not satisfied [with T = int]
void foo(const T&) {
This, of course, is much more readable that some page-long, cryptic error message arising from an error from within the function caused by some operation that simply cannot be called on an int.
Using C++20 concepts, you could also write the template like so:
template<typename T>
requires std::is_base_of_v<Base, T>
void foo(const T&) {
}
Concepts greatly improve readability as template metaprogramming code can quickly get very lenghty and hard to read.
If you need to check more complex properties of a type, you can combine multiple type traits or write your own, which can get very complicated depending on what it is that you need to check.
I hope that clears things up a little bit for you!
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.
In his answer to this question and the comment section, Johannes Schaub says there's a "match error" when trying to do template type deduction for a function template that requires more arguments than have been passed:
template<class T>
void foo(T, int);
foo(42); // the template specialization foo<int>(int, int) is not viable
In the context of the other question, what's relevant is whether or not type deduction for the function template succeeds (and substitution takes place):
template<class T>
struct has_no_nested_type {};
// I think you need some specialization for which the following class template
// `non_immediate_context` can be instantiated, otherwise the program is
// ill-formed, NDR
template<>
struct has_no_nested_type<double>
{ using type = double; };
// make the error appear NOT in the immediate context
template<class T>
struct non_immediate_context
{
using type = typename has_no_nested_type<T>::type;
};
template<class T>
typename non_immediate_context<T>::type
foo(T, int) { return {}; }
template<class T>
bool foo(T) { return {}; }
int main()
{
foo(42); // well-formed? clang++3.5 and g++4.8.2 accept it
foo<int>(42); // well-formed? clang++3.5 accepts it, but not g++4.8.2
}
When instantiating the first function template foo for T == int, the substitution produces an invalid type not in the immediate context of foo. This leads to a hard error (this is what the related question is about.)
However, when letting foo deduce its template-argument, g++ and clang++ agree that no instantiation takes place. As Johannes Schaub explains, this is because there is a "match error".
Question: What is a "match error", and where and how is it specified in the Standard?
Altenative question: Why is there a difference between foo(42) and foo<int>(42) for g++?
What I've found / tried so far:
[over.match.funcs]/7 and [temp.over] seem to describe the overload resolution specifics for function templates. The latter seem to mandate the substitution of template parameters for foo.
Interestingly, [over.match.funcs]/7 triggers the process described in [temp.over] before checking for viability of the function template (specialization).
Similarly, type deduction does not to take into account, say, default function arguments (other than making them a non-deduced context). It seems not to be concerned with viability, as far as I can tell.
Another possibly important aspect is how type deduction is specified. It acts on single function parameters, but I don't see where the distinction is made between parameter types that contain / are dependent on template parameters (like T const&) and those which aren't (like int).
Yet, g++ makes a difference between explicitly specifying the template parameter (hard error) and letting them be deduced (deduction failure / SFINAE). Why?
What I've summarized is the process described at 14.8.2.1p1
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 our case, we have for P (T, int) and for A, we have (int). For the first pair of P/A, which is T against int, we can match T to int (by the process described in 14.8.2.5). But for the second "pair", we have int but have no counterpart. Thus deduction cannot be made for this "pair".
Thereby, by 14.8.2.5p2, "If type deduction cannot be done for any P/A pair, ..., template
argument deduction fails.".
You then won't ever come to the point where you substitute template arguments into the function template.
This can all probably described more precisely in the Standard (IMO), but I believe this is how one could implement things to match the actual behavior of Clang and GCC and it seems a reasonable interpretation of the Standardese.
In this article, they are saying (c) explicit specialization of (b). My doubt is why can't we say it is explicit specialization of (a) ? because we can specialize the template for any particular type. so while specializing for int*, why they say (c) explicit specialization of (b) .
template<class T> // (a) a base template
void f( T );
template<class T> // (b) a second base template, overloads (a)
void f( T* ); // (function templates can't be partially
// specialized; they overload instead)
template<> // (c) explicit specialization of (b)
void f<>(int*);
Any comments will be helpful to understand the things.
If (b) didn't exist, then (c) would indeed be a valid specialisation of (a). In fact, just changing the order of the source lines so that (c) appears before the compiler has seen (b) will make it a specialisation of (a)!
Consider the following code:
int main()
{
int a;
f(&a);
return 0;
}
Now put yourself in the compiler's shoes. You need to find a matching function f with an int* argument. What do you do?
First, you try all the non-template functions called f that you know about, and see if there are any which match the argument types (in this case, int*).
If you can't get a perfect match, you look at all the base templates that you know about. In this case, there are two: f<T> and f<T*>. Note that unlike classes, function templates can't be partially specialised, so as far as the compiler is concerned these are completely separate overloads.
Clearly the f<T*> base template is a better match, so you instantiate it with T=int. Now, if you've already seen a specialisation for f<int*>, then you use that, and otherwise you generate the function.
Now here's the funny thing. If we change the order of your original code, to
template<class T> void f( T ); // (i)
template<> void f<>(int*); // (ii)
template<class T> void f( T* ); // (iii)
then the compiler now sees (ii) as a specialisation of (i) -- because it processes things in order, and at the point it reaches (ii) it doesn't know that (iii) exists yet! But since it only matches on base templates, it decides that (iii) is a better match than (i) -- and now (iii) does not have any specialisations, so you get the default instantiation.
It's all pretty confusing, and can trip up even the most experienced C++ programmers at times. So the basic rule is this: don't specialise function templates, but use normal function overloading instead. A regular old, non-template
void f(int*);
would be matched before anything else, and avoids this whole mess.
EDIT: n.m. requested references to the standard in the comments. I'm afraid I only have the C++03 version to hand, but here goes:
Paragraph 4.7.3.3: "A declaration of a function template or class template being explicitly specialized shall be in scope at the point of declaration of an explicit specialization.".
That's why in the above example, (ii) cannot be considered as an explicit specialisation of (iii), because (iii) is not yet in scope.
Section 4.8.3: "When a call to that [function] name is written... 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."
In other words, (as I read it, anyway) it always looks at each base template no matter what -- providing an explicit specialisation makes no difference.
The same paragraph goes on: "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."
So it's only at this point (as I read it) that explicit specialisations get taken into account.
Lastly, and perhaps most importantly in this case, section 13.3.3 deals with choosing the "best viable function" in the overload set. Two items are relevant:
F1 is better than F2 if: "F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.5.2". This is why the f<T*> version gets picked ahead of the f<T> version when trying to match f(int*) -- because it's a "more specialised" template
F1 is better than F2 if: "F1 is a non-template function and F2 is a function template specialization", which was the basis for my advice at the end of the original answer.
Phew!
I define a method like so:
template <class ArgT>
void foo(ArgT arg, ::boost::function< void(ArgT) > func)
{
func(arg);
}
and use it like this --for instance--:
foo(2, [](int i) -> void { cout << i << endl; });
Why can't the compiler deduce the type since it's definitely an int?
I get 'void foo(ArgT,boost::function<void(ArgT)>)' : could not deduce template argument for 'boost::function<void(ArgT)>' from 'anonymous-namespace'::<lambda0>'.
While C++ lambdas are strictly monomorphic, they are merely shorthand for function objects (aka functors), and in general functors can be polymorphic; i.e., their call operators can be overloaded or templated. As a result, functors (and, consequently, lambdas) are never implicitly convertible to templated std::function<> (or boost::function<>) instances because functors' operator() argument types are not automatically inferable.
To phrase it slightly differently, the natural type of your lambda expression is a functor with a parameterless constructor and an operator() with the signature void operator ()(int) const. However obvious this fact may be to you and I, it's not automatically inferrable that ArgT should resolve to int because lambdas are functors and functors' operator()s are possible to overload and template.
TL;DR: What you want isn't possible.
You want a conversion from the lambda function to boost::function<void(ArgT)> where ArgT is to be deduced. As a general rule, you cannot have type deduction and conversion in the same argument of a function: no conversions take place when deducing a template parameter.
The reasoning behind this is as follows. There are three types involved here: (1) the template parameter, (2) the function parameter type, (3) the passed object type. Two of the types (1 and 2) can be deduced from one another, but both are unknown. If the compiler can assume 2 and 3 are the same type, the problem is solved, but if all the compiler knows is that 3 can be converted to 2 there could be any number of possible solutions, and the compiler is not expected to solve the problem. In practice we know that in this particular case there is only one possible solution, but the standard does not make a distinction between cases.
The rule above applies in all deducible contexts, even if the template parameter can be deduced from another function parameter. The solution here is make the relevant function parameter a non-deducible context, ie a context in which the compiler will never attempt to deduce the template parameter from the function parameter. This can be done as follows:
template <class T> struct identity { typename T type; };
template <class ArgT>
void foo(ArgT arg, typename identity<::boost::function<void(ArgT)>>::type func)
{
func(arg);
}