Per [temp.deduct.call]/5
These alternatives ([temp.deduct.call]/4) are considered only
if type deduction would otherwise fail. If they yield more than one
possible deduced A, the type deduction fails. [ Note: If a
template-parameter is not used in any of the function parameters of a
function template, or is used only in a non-deduced context, its
corresponding template-argument cannot be deduced from a function call
and the template-argument must be explicitly specified. — end note ]
My question:
How these alternative deductions can yield more than one possible "deduced A"?
Please, support the answer with an example that triggers this case.
template<typename>
struct B {};
struct D : B<int>, B<double> {};
template<typename T>
void f(B<T>);
int main()
{
f(D{});
}
[temp.deduct.call]/(4.3):
If P is a class and P has the form simple-template-id, then the transformed A can be a derived class D of the deduced A.
applies here, and 2 deduced As are possible: B<int> or B<double>. So the type deduction fails.
Bonus example: interaction with overload resolution:
template<typename>
struct A {};
template<typename>
struct B {};
struct D1 : A<int>, B<int> {};
struct D2 : A<int>, B<int>, B<double> {};
template<typename T>
void f(A<T>); // 1
template<typename T>
void f(B<T>); // 2
int main()
{
f(D1{}); // error, ambiguous between 1 and 2
f(D2{}); // calls specialization of 1, because deduction for 2 fails
}
How these alternative deductions can yield more than one possible "deduced A"? Please, support the answer with an example that triggers this case.
An example of this can be:
template<typename T>
void f(T a, T b)
{
}
int main()
{
f(3, 5.5); //deduced A as int from first deduction while double from second deduction
return 0;
}
Here we have more than one possible "deduced A". From the first argument we've int and from the second argument b we have double.
Related
The following program:
#include <type_traits>
template<typename T, bool b>
struct S{
S() = default;
template<bool sfinae = true,
typename = std::enable_if_t<sfinae && !std::is_const<T>::value>>
operator S<T const, b>() { return S<T const, b>{}; }
};
template<typename T, bool b1, bool b2>
void f(S<const std::type_identity_t<T>, b1>,
// ^- T in non-deduced context for func-param #1
S<T, b2>)
// ^- T deduced from here
{}
int main() {
S<int, true> s1{};
S<int, false> s2{};
f(s1, s2);
}
is accepted by GCC (11.2) but rejected by Clang (13) and MSVC (19.latest), all for -std=c++20 / /std:c++20 (DEMO).
What compiler is correct here?
This is governed by [temp.deduct.call], particularly /4:
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: [...]
In the OP's example, A is S<const int, true> and the (transformed) A is S<int, int>, and none of these [three] cases applies here, meaning deduction fails.
We may experiment with this by tweaking the program such that the deduced A vs tranformed A difference falls under one of the three cases; say [temp.deduct.call]/4.3
If P is a class and P has the form simple-template-id, then the transformed A can be a derived class D of the deduced A. [...]
#include <type_traits>
template<typename T, bool b>
struct S : S<const T, b> {};
template<typename T, bool b>
struct S<const T, b> {};
template<typename T, bool b1, bool b2>
void f(S<const std::type_identity_t<T>, b1>, S<T, b2>){}
int main() {
S<int, true> s1{};
S<int, true> s2{};
f(s1, s2);
}
This program is correctly accepted by all three compilers (DEMO).
Thus, GCC most likely has a bug here, as the error argued for above is diagnosable (not ill-formed NDR). As I could not find an open bug for the issues, I've filed:
Bug 103333 - [accepts-invalid] function template argument deduction for incompatible 'transformed A' / 'deduced A' pair
We may also note that [temp.arg.explicit]/7 contains a special case where implicit conversions are allowed to convert an argument type to the function parameter type:
Implicit conversions ([conv]) 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.
This does not apply in OP's example, though, as the (function) parameter type S<const std::type_identity_t<T>, b1> contains also the (non-type) template parameter b1, which participates in template argument deduction.
However in the following program:
#include <type_traits>
template<typename T>
struct S{
S() = default;
template<bool sfinae = true,
typename = std::enable_if_t<sfinae && !std::is_const<T>::value>>
operator S<T const>() { return S<T const>{}; }
};
template<typename T>
void f(S<const std::type_identity_t<T>>, S<T>) {}
int main() {
S<int> s1{};
S<int> s2{};
f(s1, s2);
}
the (function) parameter type is A<std::type_identity_t<T>>, and the only template parameter in it is T which does not participate in template argument deduction for that parameter-argument pair (P/A). Thus, [temp.arg.explicit]/7 do apply here and the program is well-formed.
Is there a way to make template deduction work with (implicit) conversion? Like the following example:
template<typename T> struct A {};
template<typename T> struct B
{
B(A<T>); // implicit A->B conversion
};
template<typename... Ts> void fun(B<Ts>...);
int main()
{
A<int> a;
fun(B(a)); // works
fun(a); // does not work (deduction failure)
}
My thoughts:
If A is a subclass of B, everything works. That means that deduction can do implicit conversion using upcasting. So it seems weird that it can not do implicit conversion using a constructor.
Overloading fun for A and B is possible in principle, but for multiple parameters, there are just too many combinations
Adding a deduction guideline (template<typename T> B(A<T>)->B<T>;) does not change anything.
EDIT: some context:
In my actual code, A is a (large) container, and B is a lightweight non-owning view object. The situation is similar to the fact that std::vector<T> can not be implicity converted to std::span<T> during deduction of T, even though for any concrete T, such a conversion exists.
Template argument deduction does not consider any potential type conversions - mainly because deduction happens before any such conversions can happen.
Basically, when the compiler sees fun(a), it first gathers a set of foo functions that are eligible to service that call. If a foo function template is found, the compiler tries to generate a concrete function from it by substituting the template arguments with the types of arguments passed in the call statement. That's where the template argument deduction happens. No type conversion can happen here because there is no concrete type to convert to. In your example, B<T> is not a type to convert to because T is unknown and it cannot be discovered from an argument of type A<int>. It is also not known whether an arbitrary instance of B<T> will be constructible from A<int> because different specializations of B may have different sets of constructors. Hence the deduction fails in your case.
If the deduction succeeds, the concrete function (with the deduced argument types) is added to the candidate set.
When the set of candidates is gathered, then the best matching candidate is chosen. At this point argument conversions are considered. The conversions are possible since the candidates are no longer templates, and the target types for conversions are known.
What you could do to work around it is to allow the compiler to deduce the template as well and then construct B<T> explicitly:
template<typename... Ts> void fun_impl(B<Ts>...);
template<template<typename> class X, typename... Ts>
std::enable_if_t<(std::is_constructible_v<B<Ts>, X<Ts>> && ...)> fun(X<Ts>... args)
{
fun_impl(B<Ts>(args)...);
}
int main()
{
A<int> a;
fun(B(a)); // works, instantiates fun<B, int>
fun(a); // works, instantiates fun<A, int>
}
I think you can provide different overload of func then you come up with and solve your problem of multiple possible conversions.
template<typename T> struct A {};
template<typename T> struct C {};
template<typename T> struct B
{
B(A<T>) {}
B(C<T>) {}
};
template<typename... Ts>
void fun(B<Ts>...)
{
LOGFUN;
}
template<typename... Ts>
void fun(Ts...arg)
{
LOGFUN;
fun(B{arg}...);
}
int main()
{
A<int> a;
C<int> c;
fun(B(a));
fun(a);
fun(c, a, c);
}
https://godbolt.org/z/ne31z8Kfa
In the following code I have a class template and its specialization.
template<size_t U>
struct Foo
{
Foo(double(&u)[U]) : u{ u } {}
double(&u)[U];
};
template<>
struct Foo<1>
{
double &u;
bool specialized = true;
Foo(double &u) : u{ u } {}
};
If I attempt to create an instance with deduced template arguments, then the constructor arguments will only be matched against the generic Foo object.
double s[7] = { 1, 2, 3, 4, 5, 6, 7 };
Foo f(s); // will deduce s is of type Foo<7>
double t = 5.;
Foo g(t); // no instance matches the argument list!
Foo<1> g(t); // I must explicitly tell it I'm using the specialization
Of course, if the specialized class had the same constructor parameters, and I did something like Foo g(t) where t is type double[1], the instance would be of the specialized type.
So, how come the specialized constructor is also not resolved (or otherwise part of the set of constructors) in this case?
Only primary template is considered for Implicitly-generated deduction guides, but you can add deduction guide yourself:
Foo(double &u) -> Foo<1>;
Demo
Notice also that
double t[1] = {5.};
Foo g(t); // deduce Foo<1>, but fails as specialization doesn't have compatible constructor
I have the following code:
#include <iostream>
template <class T, typename U = void> class A;
template <class T>
class C
{
public:
typedef T Var_t;
};
template <class T>
class B : public C<T>
{
};
template <class T>
class A<B<T>>
{
public:
A() { std::cout << "Here." << std::endl; }
};
template <class T>
class A<T, typename std::enable_if<
std::is_base_of<C<typename T::Var_t>, T>::value>
::type>
{
public:
A() { std::cout << "There." << std::endl;}
};
int main()
{
A<B<int>> a;
return 0;
}
When the compiler tries to instantiate the second partial specialization with the parameter B<int>, std::is_base_of<C<int>, B<int>>::value is true, and therefore the std::enable_if<...>::type returns void (the default type if one isn't specified). This causes an "ambiguous partial specialization" error as the compiler can't decide between the first and second partial specializations. So far, so good. However, when I replace the code within the std::enable_if to simply be true (i.e., the second partial specialization is just template <class T> class A<T, typename std::enable_if<true>::type>), the code compiles and runs. It outputs "Here", indicating the first specialization was chosen.
My question is: If they both evaluate to void in the end, why does the behavior of std::enable_if<true>::type differ to that of std::enable_if<std::is_base_of<...>::value>::type?
This behavior has been tested and verified on Ideone here.
In the std::enable_if<true>::type case your code defines two specialisations of the class A namely:
A<B<T>, void>
A<T, std::enable_if<true>::type>.
Those two specialisations are quite distinct from each other. The first specialisation is narrowly focused on the type B<T> while the second specialisation is more general fitting any type at all. Also, in the second specialisation the std::enable_if expression does not depend on T in any way.
For any declaration A<X> a; the type X will either match B<something> or not. If it matches B<something> then the first specialisation will be used because it is "more specialised". If X does not match B<something> then the second, more general specialisation will be used. Either way you don't get the ambiguous error.
For more details see the discussion of Partial Ordering in partial template specialization
Now let's consider the std::enable_if<std::is_base_of<...>::value>::type case.
You still have two specialisations but the second specialisation is now conditional on the enable_if which in turn depends on the parameter T.
A<B<T>, void>
A<T, std::enable_if<...>>.
The type B<int> now matches both specialisations (to some equal extent). Obviously it matches the A<B<T>>, void> specialisation but it also matches the A<T, std::enable_if...>> specialisation because B<int> is a type which satisfies the conditions imposed by the std::enable_if expression.
That gives you two equally valid specialisations that are candidates for your declaration of the variable a and so you get the "ambiguous partial specialization" error.
It might help make all this a bit more concrete if you added two more declarations to main
A<C<int>> x;
A<int> y;
In the std::enable_if<true> case this will compile and both declarations will call the "there" constructor.
In the more complex case the declaration of x will compile and invoke the "there" constructor but the declaration of y will get a compiler error.
There is no int::Var_t so the std::enable_if expression will get a substitution failure and SFINAE will hide that specialisation. That means there won't be any specialisation that fits int and you'll get the error aggregate ‘A<int> y’ has incomplete type and cannot be defined
I am trying to do a simple partial template specialization, but I get errors on g++4.4.7, g++4.8.5, clang++3.8.0. Whenever I mention compiler(s) error, I mean the output of all of these, as they always agree.
I am using C++03, compiling without any option.
The code:
#include <iostream>
template <typename T, typename X, typename G>
struct A {};
template <typename T, typename X>
struct A<T, X, void> { A() : n(1) {} X n; T b; };
template <typename X>
struct A<X, void, void> { A() : n(2) {} X n; };
int main() {
A<int, float> one;
A<int> two;
std::cout << one.n << " | " << two.n << "\n";
return 0;
}
Question 1: This code fails to compile. The compilers say that A<int, float> and A<int> are wrong as A requires 3 templates parameters. Why?
If I change the original declaration to
template <typename T, typename X = void, typename G = void>
struct A {};
The code compiles and the output is: 1 | 2.
What happens is that the compiler in a first step matches one and two type to the not specialized A, but then it correctly decides to use the code of the partially specialized class one would expect it to use. But it should not need the defaults.
I then decide to change the last partial specialization switching the first and second parameter:
template <typename X>
struct A<void, X, void> { A() : n(2) {} X n; };
I would expect this to change nothing, but the compilers disagree. The clearest output between the 3 is here reported:
a.cpp:7:40: error: field has incomplete type 'void'
struct A<T, X, void> { A() : n(1) {} X n; T b; };
^
a.cpp:14:10: note: in instantiation of template class 'A<int, void, void>' requested here
A<int> two;
^
1 error generated.
Question 2: Why are the compilers considering the two variable an instance of the partial specialization of A that specializes only one argument?
Note that is the "2nd matching", because if I only use 1 default template argument, the compiler will go back to complaining about the fact that 3 template parameters are needed.
Thanks.
Question 1: This code fails to compile. The compilers say that A<int, float> and A<int> are wrong as A requires 3 templates parameters. Why?
Because A requires 3 template parameters. You declared A as:
template <typename T, typename X, typename G>
struct A {};
There is no two- or one-template parameter version of A. There are versions specialized on some of the types being void, but that's still a parameter - not an absence of parameter.
When you add the defaults, then A<int, float> evaluates as A<int, float, void>, which is a valid instantiation - and picks the specialization which sets n to 1.
You're misunderstanding how specialization works. Specialization doesn't change the number of template parameters. It's just a way of adding special functionality depending on what the template parameters end up being.
Question 2: Why are the compilers considering the two variable an instance of the partial specialization of A that specializes only one argument?
We have three choices
template <T, X, G> struct A; // the primary
template <T, X, void> struct A; // (1)
template <void, X, void> struct A; // (2)
When we instantiate A<int>, that is the same as A<int, void, void> when we add in the default parameters. That does not match (2) - because that one requires the first parameter to be void and yours is int. (1) is a better match than the primary since it's more specialized. But then, (1) has a member of type X and in this case X is deduced as void (from the default parameter), and that's not allowed.