Class template argument deduction not working with alias template
This was a feature that we considered when formulating the proposal, but it was eventually cut from the C++17 feature set because we didn't yet have a good enough design for it. In particular, there are some subtleties regarding how you select and transform deduction guides from the aliased template into deduction guides for the alias template. There are also open questions as to how to behave if the alias template is not a simple alias for another template. Some examples:
template<typename T> struct Q { Q(T); }; // #1
template<typename T> struct Q<T*> { Q(T); }; // #2
template<typename U> using QP = Q<U*>;
int *x;
Q p = x; // deduces Q<int*> using #1, ill-formed
QP q = x; // deduces Q<int*> using #1, or
// deduces Q<int**> using #2?
template<typename T> Q(T) -> Q<T>; // #3
QP r = x; // can we use deduction guide #3 here?
template<typename T> Q(T*) -> Q<T**>; // #4
int **y;
QP s = y; // can we use deduction guide #4 here?
template<typename T> struct A { typedef T type; struct Y {}; };
template<typename T> using X = typename A<T>::type;
template<typename T> using Y = typename A<T>::Y;
X x = 4; // can this deduce T == int?
Y y = A<int>::Y(); // can this deduce T == int?
There are decent answers to the above questions, but tackling them adds complexity, and it seemed preferable to disallow deduction for alias templates for C++17 rather than rush something flawed in.
Update [C++20]: This topic was revisited for C++20, and we approved P1814R0, which permits class template argument deduction for alias templates.
The original example is now valid. For the examples above:
CTAD still only considers constructors from the primary template. So in
QP q = x;
, #2 is not considered, and instead #1's constructor is used. That constructor is implicitly converted into a guide forQ
:template<typename T> Q(T) -> Q<T>;
which is then converted into a guide for the alias template
QP
by deducing the right-hand side of the guide forQ
(Q<T>
) from the right-hand side of the alias template (Q<U*>
), which deducesT
=U*
, then substituting that back into the guide, thereby producing the equivalent of:template<typename U> Q(U*) -> Q<U*>;
// ... which is effectively ...
template<typename U> QP(U*) -> QP<U>;
// ... except that explicit deduction guides are not
// permitted for alias templatesThat guide is then used to deduce the type of
q
, which deducesU
=int
, so the type ofq
isQ<int*>
, so the initialization ofq
is ill-formed.The initialization of
r
does consider deduction guide #3, which is transformed into a guide forQP
as described aboveThe initialization of
s
does consider deduction guide #4; deducingQ<T**>
fromQ<U*>
deduces nothing, so we retain the deduction guidetemplate<typename T> Q(T*) -> Q<T**>;
as-is, but add a constraint that the result of deduction must match the form of
QP
. We then deduceT
=int
, substitute that in to compute a result type ofQ<int**>
, and check that we can deduceQP<U>
fromQ<int**>
, which we can. So the type ofs
is deduced asQ<int**>
.CTAD for alias templates is only supported where the right-hand side of the alias template is a simple-template-id (of the form
maybe::stuff::templatename<args>
). So neitherX
norY
is deducible.
Can alias templates have default template parameters?
C++20 introduces CTAD for alias templates
An alias template may indeed have default template arguments, and it is legal to have default template arguments to template parameters that are followed by a template parameter pack, as per [temp.param]/14:
If a template-parameter of a class template, variable template, or alias template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack. [...]
The default template argument is a red herring, however, and the key here is whether class template argument deduction is valid or not for alias templates, and we may minimize your example to the following one:
#include <tuple>
template <typename T>
using tpl = std::tuple<T>;
tpl x{1}; // should deduce tpl<int> in C++20
// Clang: error
// GCC 9.3: error
// GCC 10.1: ICE / internal compiler error
As per P1814R0(1), which was accepted for C++20, the minimal example above is indeed legal, but Clang is yet to implement P1814R0, explaining why Clang rejects it. GCC, on the other hand, lists P1814R0 as implemented for GCC 10, meaning it should accept it for C++20.
(1) As per C++20 and P1814R0 (Wording for Class Template Argument Deduction for Alias Templates), (/wording for original proposal P1021R4) CTAD is applicable also for alias templates, whilst however not allowing explicit deduction guides for them.
In C++17 you need to include the template argument list (even if it's empty) when using alias templates - there is no equivalent to class template argument deduction for alias templates in C++17:
#include <tuple>
template <typename T = int, typename... Ts>
using tpl = std::tuple<T, Ts...>;
tpl<> x; // OK in GCC and Clang
An ICE (internal compiler error) is always a bug, no matter if the code is ill-formed or well-formed, and as noted above GCC emits an ICE only for 10.1 and later, whereas it yields an error for previous releases.
Thus, GCC apparently have a ICE regression for 10.1 (which was suspiciously listed as the target when CTAD for alias templates were implemented). It is at the very least related to the following bug report:
- Bug 96199 - [10/11 Regression] internal compiler error: in tsubst_copy with CTAD for alias templates
Which however is listed as resolved, whereas your example still yields an ICE for a GCC trunk that includes the fix to 96199.
We may finally note that GCC successfully applies CTAD for the alias template where we only use a template parameter pack:
#include <tuple>
template <typename... Ts>
using tpl = std::tuple<Ts...>;
tpl x{1}; // OK
but that if we replace std::tuple
by std::vector
in the minimal example:
#include <vector>
template <typename T>
using vec = std::vector<T>;
vec x{{1}}; // GCC 10.1: ICE
we get another kind of ICE for GCC 10.1 (and forward), whereas adding a default template argument and replacing the braced-direct-initialization with default-initialization is accepted.
#include <vector>
template <typename T = int>
using vec = std::vector<T>;
vec x; // GCC 10.1: OK
C++ Template Argument Deduction with Additional Specified Template Arguments
Unfortunately, "Class template argument deduction is only performed if no template argument list is present. If a template argument list is specified, deduction does not take place." - https://en.cppreference.com/w/cpp/language/class_template_argument_deduction
You could use a wrapper function to do what you want, but it won't work with the constructor itself.
Making guides for function template argument deduction in C++
OK I got the answer actually functions can't have deduction guides. It only works with class templates. Thanks for pointing me to the right direction.
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);
variadic template deduction with two template argument fails
If Dims
should equal the number of parameters passed, you should use the sizeof...
operator instead of letting the caller specify the size.
CTAD (class template argument deduction) only works when all template arguments of the class are deduced.
The workaround is to let all arguments be deduced. You can wrap the class that deduces the indices into another one that can have the size given explicitly:
#include <iostream>
#include <tuple>
template <size_t Dims>
struct wrapper {
template <std::same_as<char> ... Indices>
struct MIndices {
std::tuple<Indices...> indices;
MIndices() = delete;
constexpr explicit MIndices(Indices... args) : indices(args...) {
}
};
};
int main() {
wrapper<4>::MIndices myI('i', 'j', 'l', 'z');
}
Live Demo
Type aliases and template template argument deduction
Yes, that is intentional. Alias templates are indeed, as you said, somewhat of a "second class citizen". To start with, alias templates cannot be specialized, that's a real big hint right there.
Now, their "lower grade" as evident in your example is all about [temp.alias]/2:
When a template-id refers to the specialization of an alias
template, it is equivalent to the associated type obtained by
substitution of its template-arguments for the template-parameters
in the type-id of the alias template. [ Note: An alias template name
is never deduced. — end note ]
The above means, that when you write Matched<Alias<int>>
, since Alias<int>
refers to a specialization of the alias template, it's equivalent to directly writing Matched<std::tuple<int,int>>
. And it's quite obvious why that doesn't match the specialized variable template.
It's not an oversight, nor is it going to be fixed. Alias templates are there to provide a shorthand for more complex template expressions. And you wouldn't want the wrong overload to be called, or the wrong template specialization instantiated, because you used a shorthand instead of the whole complex expression.
Deduction guide with type alias after -
Clang and ICC are correct here: the restriction on how to name the template and type is phrased syntactically. C++20 allows “class template” argument deduction for alias templates, but does not support (separate) deduction guides for them.
Related Topics
Why Do I Get _Crtisvalidheappointer(Block) And/Or Is_Block_Type_Valid(Header->_Block_Use) Assertions
How to Tell If the C Function Atoi Failed or If It Was a String of Zeros
How to Make a Portable Isnan/Isinf Function
Universal Less<> for Pointers in C++ Standard
Why Doesn't Std::String Provide Implicit Conversion to Char*
What Is the Proper Way of Doing Event Handling in C++
When Is a Function Try Block Useful
Class Template Argument Deduction Not Working with Alias Template
Understanding Double Dispatch C++
When Is It Worthwhile to Use Bit Fields
On Local and Global Static Variables in C++
What Does It Mean for a C++ Function to Be Inline
What's a Good Hash Function for English Words
How to Change the Background Color of a Button Winapi C++
Can Class Template Constructors Have a Redundant Template Parameter List in C++20
Compiling with -Static-Libgcc -Static-Libstdc++ Still Results in Dynamic Dependency on Libc.So