Workaround for template argument deduction in non-deduced context
You can move the operator into the inner class body and put friend
before it. Then replace the parameter type by just inner
.
Another technique is to derive inner from a CRTP base parameterized by inner. Then make the parameter type the CRTP class and cast the parameter reference to the derived inner
class, the type of which is given by the template argument you deduce.
Why does an optional argument in a template constructor for enable_if help the compiler to deduce the template parameter?
Template argument deduction does not work this way.
Suppose you have a template and a function using a type alias of that template:
template <typename T>
struct foo;
template <typename S>
void bar(foo<S>::type x) {}
When you call the function, eg foo(1)
then the compiler will not try all instantiations of foo
to see if any has a type
that matches the type of 1
. And it cannot do that because foo::type
is not necessarily unambiguous. It could be that different instantiations have the same foo<T>::type
:
template <>
struct foo<int> { using type = int; };
template <>
struct foo<double> { using type = int; };
Instead of even attempting this route and potentially resulting in ambiguity, foo<S>::type x
is a nondeduced context. For details see What is a nondeduced context?.
Template argument deduction from inherited type
Is there a way to get the compiler to deduce the template parameter in this case?
No. Not in C++14 (or even C++20).
And what would you call this behaviour?
Standard compliant. To be specific, this paragraph applies:
[temp.deduct.call]
4 In general, the deduction process attempts to find template
argument values that will make the deducedA
identical toA
(after
the typeA
is transformed as described above). However, there are
three cases that allow a difference:
- If the original
P
is a reference type, the deducedA
(i.e., the type referred to by the reference) can be more cv-qualified than the
transformedA
.- The transformed
A
can be another pointer or pointer to member type that can be converted to the deducedA
via a qualification
conversion ([conv.qual]).- If
P
is a class andP
has the form simple-template-id, then the transformedA
can be a derived class of the deducedA
.
Likewise, ifP
is a pointer to a class of the form
simple-template-id, the transformedA
can be a pointer to a derived class pointed to by the deducedA
.
This is an exhaustive list of cases where a template argument can be validly deduced from a function argument even if it doesn't match the pattern of the function parameter exactly. The first and second bullets deal with things like
template<class A1> void func(A1&){}
template<class A2> void func(A2*){}
int main() {
const int i = 1;
func(i); // A1 = const int
func(&i); // A2 = const int
}
The third bullet is the one that is closest to our case. A class derived from a template specialization can be used to deduce a template parameter pertaining to its base. Why doesn't it work in your case? Because the function template parameter is unique_ptr<a<T>>
and the argument you call it with is unique_ptr<b>
. The unique_ptr
specializations are not themselves related by inheritance. So they don't match the bullet, and deduction fails.
But it doesn't mean that a wrapper like unique_ptr
prevents template argument deduction entirely. For instance:
template <typename> struct A {};
struct B : A<int> {};
template<typename> struct wrapper{};
template<> struct wrapper<B> : wrapper<A<int>> {};
template<typename T>
void do_smth(wrapper<A<T>>) {}
int main() {
do_smth(wrapper<B>{});
}
In this case, wrapper<B>
derives from wrapper<A<int>>
. So the third bullet is applicable. And by the complex (and recursive) process of template argument deduction, it allows B
to match A<T>
and deduce T = int
.
TL;DR: unique_ptr<T>
specializations cannot replicate the behavior of raw pointers. They don't inherit from the specializations of unique_ptr
over T
's bases. Maybe if reflection ever comes to C++, we'll be able to meta-program a smart pointer that does behave that way.
Why is the template argument deduction not working here?
Just as first note, typename name is used when you mention a dependent name. So you don't need it here.
template <class T>
struct S
{
typedef T& type;
};
Regarding the template instantiation, the problem is that typename S<A>::type
characterizes a nondeduced context for A. When a template parameter is used only in a nondeduced context (the case for A in your functions) it's not taken into consideration for template argument deduction. The details are at section 14.8.2.4 of the C++ Standard (2003).
To make your call work, you need to explicitly specify the type:
temp<char>(c);
Template parameter is ambiguous: could not deduce template argument
Why it fails
Currently, the template parameter Value
is deduced in two different places in the call to Apply
: from the pointer to member function argument and from the last argument. From &Foo::MyFunc
, Value
is deduced as int const&
. From f.GetValue()
, Value
is deduced as int
. This is because reference and top-level cv-qualifiers are dropped for template deduction. Since these two deductions for the parameter Value
differ, deduction fails - which removes Apply()
from the overload set, and as a result we have no viable overload.
How to fix it
The problem is that Value
is deduced in two different places, so let's just prevent that from happening. One way is to wrap one of the uses in a non-deduced context:
template <class T> struct non_deduced { using type = T; };
template <class T> using non_deduced_t = typename non_deduced<T>::type;
template<class T, class Value>
void Apply(void (T::*cb)(Value), T* obj, non_deduced_t<Value> v)
{
(obj->*cb)(v);
}
The last argument, v
, is of type non_deduced_t<Value>
which, as the name suggests, is a non-deduced context. So during the template deduction process, Value
is deduced as int const&
from the pointer to member function (as before) and now we simply plug that into the type for v
.
Alternatively, you could choose to deduce cb
as its own template parameter. At which point Apply()
just reduces to std::invoke()
.
How can I create deduction guides for template aliases in C++20?
To do what you want, C++ would have to be able to invert a Turing-complete subprogram.
Turing-complete programs are not only not-invertible, it isn't possible to determine if a given Turing-complete program is invertible or not. You can define sublanguages where they are all invertible, but those sublanguages lack Turing-complete power.
Deducing the Bar
alias argument:
template <typename T>
using Bar = Foo<1, T>;
requires inverting the 2nd template argument alias<F>
to find F
. When alias
is a trivial template alias, this is possible, permitted, and required by the C++ standard.
When alias2
evaluates is to a foo<F>::type
, such a construct is capable of turing-complete computation. Rather than have compilers try to invert such a computation, the C++ standard uniformly says "don't try". It uses "dependent type" usually to block such an inversion attempt.
In your second case, Bar
is a trivial alias of Foo
. Foo
has a deduction guide. That deduction guide tells how to get from F
to T
, so the compiler doesn't have to invert any potentially Turing-complete programs in order to determine T
.
The C++ language has a bunch of wording to permit template aliases that are just renaming of parameters or the like to act as if they were the original type. Originally even a toy alias would block a bunch of this kind of deduction; but this was found to be a bad plan. So they added text to the standard to describe what kind of template aliases where "trivial" like that, and modified the wording of deduction rules so that they would be treated as transparent.
In order to invert an arbitrary Turing-complete program (in fact, almost any structurally non-trivial type transformation) during type deduction, you must explicitly give the inversion.
Once you have accepted that, it becomes battle with syntax, not a conceptual one.
I'm unaware of the current status of user-defined template deduction guides of alias templates. Last I heard it wasn't supported, but I haven't checked recently.
Template argument deduction failure
The issue is that std::function<Output(Output,Input)>
cannot properly deduce its template arguments, because std::plus<int>
is not a std::function
.
Type-deduction only works if the template types can match up with the arguments being passed; it doesn't perform transformations that would be done through conversions/constructions; but rather takes the type in totality.
In this case, std::plus<int>
is not a std::function<int(int,int)>
, it is convertible to a std::function<int(int,int)>
. This is what is causing the deduction to fail.
The alternatives are to either break contributions to the type-deduction with an alias, to explicitly pass a std::function
type, or to accept the function as a different template argument (which may actually be more efficient)
1. Break contribution to the type-deduction with an alias
If you make an alias of the type, so that the types don't contribute to the deduction, then it can leave the first and third arguments to determine the types of the std::function
(off the top of my head; untested)
template<typename T>
struct nodeduce{ using type = T; };
template<typename T>
using nodeduce_t = typename nodeduce<T>::type;
template<class Input, class Output>
Output fold_left(Output zero, std::function<nodeduce_t<Output>(nodeduce_t<Output>,nodeduce_t<Input>)> op, std::valarray<Input> data)
{
....
}
It's a bit of an ugly approach, but it will disable std::function
from contributing to the deduction because the types are transformed through nodeduce_t
.
2. Pass as std::function
This is the simplest approach; your calling code would become:
fold_left(0, std::function<int(int,int)>(std::plus<int>{}), data);
Admittedly, it's not pretty -- but it would resolve the deduction because std::function<int(int,int)>
can directly match the input.
3. Pass as template argument
This actually has some additional performance benefits; if you accept the argument as a template type, then it also allows your function to be inlined better by the compiler (something that std::function
has issues with)
template<class Input, typename Fn, class Output>
Output fold_left(Output zero, Fn&& op, std::valarray<Input> data)
{
for (auto const& item : data) {
zero = op(zero, item);
}
return zero;
}
All of these are possible alternative that would accomplish the same task.
It's also worth mentioning that compile-time validation can be done using SFINAE with things like enable_if
or decltype
evaluations:
template<class Input, typename Fn, class Output, typename = decltype( std::declval<Output> = std::declval<Fn>( std::declval<Output>, std::declval<Input> ), void())>
Output fold_left( ... ){ ... }
The decltype(...)
syntax ensures the expression is well-formed, and if it is, then it allows this function to be used for overload resolution.
Edit: Updated the decltype
evaluation to test Output
and Input
types. With C++17, you can also use std::is_convertible_v<std::invoke_result_t<F&, Output, Input>,Output>
in an enable_if
, as suggested by Barry in a different answer.
Could not deduce template argument (vector, std::function)
Template argument deduction doesn't consider implicit conversion.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
Then 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.
SFINAE works with deduction but fails with substitution
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 toA
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 resolutionA
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.
Related Topics
Getdibits and Loop Through Pixels Using X, Y
Cast Pointer to Member Function to Normal Pointer
Can You Make Custom Operators in C++
How to Reset Std::Cin When Using It
Compare Two Float Variables in C++
*.H or *.Hpp for Your Class Definitions
C++ Preprocessor: Avoid Code Repetition of Member Variable List
Does Delete on a Pointer to a Subclass Call the Base Class Destructor
What Does {0} Mean When Initializing an Object
What's the Purpose of Using Braces (I.E. {}) for a Single-Line If or Loop
Dead Code Detection in Legacy C/C++ Project
Are There Any Better Methods to Do Permutation of String
Mingw .Exe Requires a Few Gcc Dll's Regardless of the Code
Why Should the Assignment Operator Return a Reference to the Object