Local type as template arguments in C++
Which part of the standard says that this is wrong?
That would be §14.3.1/2 from the 2003 C++ Standard:
A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template-argument for a template type-parameter.
How can make this code work?
Don't use a local type as a template argument.
Note that this restriction has been lifted in C++11, so using that language standard you are able to use a local type as a template argument.
Is it legal to declare a type as part of the template-argument for a type template-parameter?
(It seems the OP (myself) did not initially go deep enough down the type-id grammar rabbit hole)
Is it legal to declare a type as part of the template-argument for a type template-parameter?
Yes, this is legal.
If so, which part of the standard (say, C++17) governs this?
A class-key and identifier grammar sequence, say struct Tag1
, is a valid elaborated-type-specifier,
elaborated-type-specifier:
class-key attribute-specifier-seq_opt nested-name-specifier_opt identifier
...
which in turn is a valid type-specifier,
type-specifier:
elaborated-type-specifier
...
which is turn is a valid single entry of a type-specifier-seq,
type-specifier-seq:
type-specifier attribute-specifier-seq_opt
which in turn is a valid type-id,
type-id:
type-specifier-seq abstract-declarator_opt
...
and as was noted in the OP, a type-id, as per [temp.arg.type]/1, is a valid template-argument for a type template-parameter;
A template-argument for a template-parameter which is a type shall be a type-id.
Thus, the snippet
template<typename Tag>
struct Tagged {};
Tagged<struct Tag1> t1;
Tagged<struct Tag2> t2;
is well-formed.
Partial specialization of templates over non-type literal parameters in C++20: clang and gcc disagree
Let's focus on the partial specialization of is_named
:
template <typename T, size_t N, StringLiteral<N> Name>
struct is_named<named<T, Name>>: std::true_type{};
and particularly try to answer whether it violates [temp.class.spec.match]/3 or not:
If the template arguments of a partial specialization cannot be deduced because of the structure of its template-parameter-list and the template-id, the program is ill-formed.
noting that Clang apparently does not think so, and uses the single template argument to the primary template to deduce all template arguments of the partial specialization. In this particular case these template arguments are those matching its template-parameter-list:
- the template argument for the type template parameter
T
- the template argument for the non-type template parameter
N
- the template argument for the non-type template parameter
Name
As per [temp.deduct.type]/4:
[...] If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails. [...]
we can break the question down into whether all three template parameters T
, N
and Name
of the partial specialization are used in least one deduced context or not.
Starting from [temp.deduct.type]/1
Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it
P
) is compared with an actual type (call itA
), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will makeP
, after substitution of the deduced values (call it the deducedA
), compatible withA
.
and [temp.deduct.type]/3 and (again) /4:
/3 A given type P can be composed from a number of other types, templates, and non-type values:
- [...]
- A type that is a specialization of a class template (e.g.,
A<int>
) includes the types, templates, and non-type values referenced by the template argument list of the specialization. [...]/4 In most cases, the types, templates, and non-type values that are used to compose
P
participate in template argument deduction.
We can without loss of generality consider the actual type of the template argument for the single type template parameter of the primary template (which we intend to be part fo the family of types for which partial specialization to applies), say A
, as named<int, StringLiteral<5>{"ciao"}>
.
The type specified in terms of the template parameters of the partial specialization, say P
, is named<T, Name>
.
T
can be trivially deduced, matchingA
/P
asnamed<int, StringLiteral<5>{"ciao"}>
/named<T, Name>
, toint
, asT
innamed<T, Name>
is not in a non-deduced context.Name
is similarly not in a non-deduced context, and can be deduced toStringLiteral<5>{"ciao"}
.- The tricky part is
N
, which is not explicitly part ofP
, but only implicitly so via the template parameterName
. However, here we may simply apply the deduction rules recursively:Name
has been deduced toStringLiteral<5>{"ciao"}
, meaning we consider a newA
/P
pairStringLiteral<5>
/StringLiteral<N>
, in which N is non in a non-deducible context, andN
can thus ultimately be deduced to5
.
And, why do the compilers disagree about it? Who's got the bug?
Consequently, Clang (as well as MSVC) are correct to accept your original variant, whereas GCC is wrong to reject it (rejects-valid bug).
A more minimal example which is (correctly) accepted by Clang and MSVC, and (incorrectly) rejected by GCC is:
template<int N> struct S {};
template<S s> struct U {};
template<typename> struct V { V() = delete; };
template <int N, S<N> s>
struct V<U<s>> {};
V<U<S<0>{}>> v{};
// Expected: use partial specialization #1
// GCC actual: error (rejects-valid): use of deleted function
and I have filed a bug report using this example:
- Bug 99699 - Type deduction failure for deducing a non-type template parameter via another deducible structural type (class template specialization) non-type template parameter
[...] removing the
size_t N
parameter in the partial specialization makes both compilers agree, [...]
In your second variant, the deduction case is not as complex as the first one, and you can use a similar analysis to see that is likewise well-formed (all template parameters of the partial specialization are deducible).
Related Topics
"Volatile" Qualifier and Compiler Reorderings
Quadruple Precision in C++ (Gcc)
How to Get the Address of the Std::Vector Buffer Start Most Elegantly
Reading Directly from an Std::Istream into an Std::String
Why Does Long Long 2147483647 + 1 = -2147483648
Is There a C++ Equivalent to Getcwd
C++: Calling Member Function via Pointer
How to Convert a String Literal to Unsigned Char Array in Visual C++
How to Iterate Over Two Vectors Simultaneously Using Boost_Foreach
#Error Please Use the /Md Switch for _Afxdll Builds
Create Shared_Ptr to Stack Object
Fatal Error C1083: Cannot Open Include File: 'Xyz.H': No Such File or Directory
Drawing in a Win32 Console on C++
What Happens When C++ Reference Leaves Its Scope
Stl Vectors with Uninitialized Storage