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.
Related
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.
For a bit of fun, I created a very basic compile-time type-value map class, as follows:
template <typename T, auto V>
struct TypeValuePair { };
template <typename... TypeValuePairs>
struct TypeValueMap
{
struct MapItems : TypeValuePairs... { };
template <typename T, auto V>
static constexpr auto Lookup(TypeValuePair<T, V>*)
{ return V; }
template <auto V, typename T>
static T Lookup(TypeValuePair<T, V>*);
template <typename T>
static constexpr auto ValueFor = Lookup<T>((MapItems*)nullptr);
template <auto V>
using TypeFor = decltype(Lookup<V>((MapItems*)nullptr));
};
to be used in a way such as this:
struct A; struct B; struct C;
enum class Values { A, B, C };
using Map = TypeValueMap<
TypeValuePair<A, Values::A>,
TypeValuePair<B, Values::B>,
TypeValuePair<C, Values::C>,
TypeValuePair<struct Other, 0>
>;
static_assert(Map::ValueFor<A> == Values::A, "");
static_assert(Map::ValueFor<B> == Values::B, "");
static_assert(Map::ValueFor<C> == Values::C, "");
static_assert(Map::ValueFor<struct Other> == 0, "");
static_assert(std::is_same<Map::TypeFor<Values::A>, A>::value, ""); //***
static_assert(std::is_same<Map::TypeFor<Values::B>, B>::value, "");
static_assert(std::is_same<Map::TypeFor<Values::C>, C>::value, "");
static_assert(std::is_same<Map::TypeFor<0>, struct Other>::value, ""); //***
Unfortunately, the two lines marked //*** fail with the error failed template argument deduction or similar on clang and g++ (the two compilers I have to hand). I can understand why this might be because Values::A has the value 0 so the two potentially collide. However, I would argue that they are in fact different types – one is plain integer, the other an enum class with underlying type integer – and so shouldn't in fact collide.
If I implement my map class differently, like so:
template <typename T, auto V>
struct TypeValuePair
{
protected:
static constexpr auto Lookup(T*)
{ return V; }
template <template <auto> class Wrapper>
static T Lookup(Wrapper<V>*);
};
template <typename... TypeValuePairs>
struct TypeValueMap
{
struct MapItems : TypeValuePairs...
{ using TypeValuePairs::Lookup...; };
template <auto> struct LookupByValue;
template <typename T>
static constexpr auto ValueFor = MapItems::Lookup((T*)nullptr);
template <auto V>
using TypeFor = decltype(MapItems::Lookup((LookupByValue<V>*)nullptr));
};
then there are no template argument deduction errors.
Therefore the question is, is the failure to deduce the template argument in the first implementation due to a bug in the compilers (given my assertion that integer and enum class should be treated as different types and not collide) or is it a misunderstanding on my side of what is possible with template argument deduction (I am not a language lawyer!), or some other bug in my implementation?
The problem is that Values::A is convertible to 0 by means of a "converted constant expression".
From the C++14 standard (n4140 section 5.19, paragraph 3, page 133):
A converted constant expression of type T is an expression, implicitly converted to a prvalue of type T, where the converted expression is a core constant expression and the implicit conversion sequence contains only user-defined conversions, lvalue-to-rvalue conversions (4.1), integral promotions (4.5), and integral conversions (4.7) other than narrowing conversions (8.5.4).
[ Note: such expressions may be used in new expressions (5.3.4), as case expressions (6.4.2), as enumerator initializers if the underlying type is fixed (7.2), as array bounds (8.3.4), and as integral or enumeration non-type template arguments (14.3). —end note ]
(my emphasis)
The effect is that there is an overload ambiguity between 0 and Values::A.
The title is a bit confusing but what I mean is this specific case:
template<class>
struct get_type_of_nontype;
template<class T, T Value, template<T> class Template>
struct get_type_of_nontype<Template<Value>> {
using type = T;
};
So I can use it like this:
#include <type_traits>
template<int I>
class int_non_type {};
static_assert(
std::is_same<typename get_type_of_nontype<int_non_type<0>>::type, int>::value,
"T is deduced to be `int` as `template<T> class Template` is `template<int> class int_non_type`"
);
This works fine in C++17. In C++14 I get the following errors:
gcc 8:
<source>:5:8: error: template parameters not deducible in partial specialization:
struct get_type_of_nontype<Template<Value>> {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:5:8: note: 'T'
clang 7:
<source>:5:8: error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization]
struct get_type_of_nontype<Template<Value>> {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:4:16: note: non-deducible template parameter 'T'
template<class T, T Value, template<T> class Template>
^
And then they both complain that struct get_type_of_nontype<int_non_type<0>> is incomplete, so typename get_type_of_non_type<int_non_type<0>>::type can't compile.
Why is this different between C++14 and C++17? Is this just a compiler bug? If not, is there a way to do this in C++14?
The Standard wording in [temp.deduct.type] paragraphs 13 and 14 changed. So yes, your example is invalid in C++14, but is allowed in C++17 thanks to a new language feature.
C++14:
A template type argument cannot be deduced from the type of a non-type template-argument.
[Example:
template<class T, T i> void f(double a[10][i]);
int v[10][20];
f(v); // error: argument for template-parameter T cannot be deduced
-- end example]
C++17:
When the value of the argument corresponding to a non-type template parameter P that is declared with a dependent type is deduced from an expression, the template parameters in the type of P are deduced from the type of the value. [Example:
template<long n> struct A { };
template<typename T> struct C;
template<typename T, T n> struct C<A<n>> {
using Q = T;
};
using R = long;
using R = C<A<2>>::Q; // OK; T was deduced to long from the
// template argument value in the type A<2>
-- end example] The type of N in the type T[N] is std::size_t. [Example:
template<typename T> struct S;
template<typename T, T n> struct S<int[n]> {
using Q = T;
};
using V = decltype(sizeof 0);
using V = S<int[42]>::Q; // OK; T was deduced to std::size_t from the type int[42]
-- end example]
[Example:
template<class T, T i> void f(int (&a)[i]);
int v[10];
void g() {
f(v); // OK: T is std::size_t
}
-- end example]
This seems a bit related to another C++17 template change: C++17 is the first version to allow placeholder types in non-type template parameters, as in template <auto Value> or template <auto* Ptr>. I'd expect compiler implementations would need some similar logic for supporting both of the two language features.
The following stripped down code doesn't work with the latest clang++5 but is accepted by g++7:
template<typename Wrapped, typename U>
struct wrapper;
template<typename Wrapped, typename U=int>
struct wrapper
{
wrapper() = default;
// Automatic deduction guide
constexpr explicit wrapper(Wrapped) noexcept {}
};
int main()
{
struct {} dummy;
constexpr auto wrapped = wrapper(dummy);
}
It fails with the following error messages:
<source>:18:30: error: no viable constructor or deduction guide for deduction of template arguments of 'wrapper'
constexpr auto wrapped = wrapper(dummy);
^
<source>:12:24: note: candidate template ignored: couldn't infer template argument 'U'
constexpr explicit wrapper(Wrapped) noexcept {}
^
<source>:4:8: note: candidate template ignored: could not match 'wrapper<Wrapped, U>' against '(anonymous struct at <source>:17:5)'
struct wrapper;
^
<source>:9:5: note: candidate function template not viable: requires 0 arguments, but 1 was provided
wrapper() = default;
^
However if I move the default template parameter =int from the class template definition to the forward declaration, everything works perfectly (U being deduced to int as expected), as if only the default template parameter in the forward declaration was taken into account when create the set of fictional function templates used by deduction guides.
I tried to read the standard wording but couldn't get much out of it for this specific case. Is only taking the default template parameter in the forward declaration the intended behaviour when generating the fictional function templates, or is this a compiler bug?
This is not a quote of the Standard per se1, but I feel confident enough to consider it an answer.
According to cppreference, on Default template arguments:
Default template arguments that appear in the declarations and the definition are merged similarly to default function arguments:
template<typename T1, typename T2 = int> class A;
template<typename T1 = int, typename T2> class A;
// the above is the same as the following:
template<typename T1 = int, typename T2 = int> class A;
But the same parameter cannot be given default arguments twice in the same scope
template<typename T = int> class X;
template<typename T = int> class X {}; // error
This implies an implicit rule: A template type argument can be given a default type in the template declaration or template definition interchangeably.
The behavior clang++5 exhibits is definitly a bug.
1) Provided by user Oliv:
[temp.param]/10
The set of default template-arguments available for use is obtained by merging the default arguments from all prior declarations of the template in the same way default function arguments are ([dcl.fct.default]). [ Example:
template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;
is equivalent to
template<class T1 = int, class T2 = int> class A;
— end example ]
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.