Local Type as Template Arguments in C++

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 it A), 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 make P, after substitution of the deduced values (call it the deduced A), compatible with A.

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, matching A/P as named<int, StringLiteral<5>{"ciao"}>/named<T, Name>, to int, as T in named<T, Name> is not in a non-deduced context.
  • Name is similarly not in a non-deduced context, and can be deduced to StringLiteral<5>{"ciao"}.
  • The tricky part is N, which is not explicitly part of P, but only implicitly so via the template parameter Name. However, here we may simply apply the deduction rules recursively: Name has been deduced to StringLiteral<5>{"ciao"}, meaning we consider a new A/P pair StringLiteral<5>/StringLiteral<N>, in which N is non in a non-deducible context, and N can thus ultimately be deduced to 5.

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



Leave a reply



Submit