Auto' as a Template Argument Placeholder for a Function Parameter

auto' as a template argument placeholder for a function parameter

This syntax is valid in the C++ Concepts Technical Specification, but not in C++20. In C++20 concepts, auto is only permitted at the top level in a function parameter type. The relevant rule is [dcl.spec.auto] paragraph 2:

A placeholder-type-specifier of the form type-constraint[opt] auto can be used as a decl-specifier of the decl-specifier-seq of a parameter-declaration of a function declaration or lambda-expression and, if it is not the auto type-specifier introducing a trailing-return-type (see below), is a generic parameter type placeholder of the function declaration or lambda-expression. [Note: Having a generic parameter type placeholder signifies that the function is an abbreviated function template (9.3.3.5 [dcl.fct]) or the lambda is a generic lambda (7.5.5 [expr.prim.lambda]). —end note]

(If you check the wording in the most recent working draft at the time of writing, you will find a somewhat different rule. The above rule was modified by core issue 2447, which was voted into the C++20 final draft at the Prague committee meeting a week ago.)

The decl-specifiers in a function parameter are the initial sequence of keywords and type names at the start of the parameter declaration. The above rule allows auto there at the top level:

void f(auto x);

... but only as a decl-specifier. auto is not permitted when nested within a decl-specifier:

void f(std::vector<auto> x);

... and is also not permitted elsewhere in the parameter type:

void f(void (*p)(auto));

Template parameters of function type with auto return type arguments of previous template parameter types

This is gcc's bug. (Bug 83417)

It seems gcc failing deducing the type of auto when using the 1st template parameter T as function parameter; you can use std::type_identity (since C++20) to exclude it from participating in template argument deduction.

// workaround for gcc
template <typename T, auto (*Func)(std::type_identity_t<T>)>
struct foo {
void bar(T t) { Func(t); }
};

LIVE

BTW: Clang 10 seems working well.

Are placeholders types of non-type template parameters interchangeable in case of template template parameter

The original answer here had Foo<Bar> ill-formed, I actually now think it's well-formed. But ultimately, clang bug based.


I actually think even Foo<Bar> is ill-formed. The new rules, following P0522 are that:

A template-argument matches a template template-parameter P when P is at least as specialized as the template-argument A

where:

A template template-parameter P is at least as specialized as a template template-argument A if, given the following rewrite to two function templates, the function template corresponding to P is at least as specialized as the function template corresponding to A according to the partial ordering rules for function templates ([temp.func.order]). Given an invented class template X with the template parameter list of A (including default arguments):

  • Each of the two function templates has the same template parameters, respectively, as P or A.
  • Each function template has a single function parameter whose type is a specialization of X with template arguments corresponding to the template parameters from the respective function template where, for each template parameter PP in the template parameter list of the function template, a corresponding template argument AA is formed. If PP declares a parameter pack, then AA is the pack expansion PP... ([temp.variadic]); otherwise, AA is the id-expression PP.

If the rewrite produces an invalid type, then P is not at least as specialized as A.

Which means that to verify if Foo<Bar> itself is okay, we synthesize:

template <decltype(auto) I> struct X;

template <auto I> void __f(X<I> ); // P
template <decltype(auto) I> void __f(X<I> ); // A

All the types here are valid (so the last statement doesn't apply). Now, typically when we do partial ordering it's in the context of either overload resolution or picking a class template specialization, in which case what we're looking for is the "more specialized" function template, where F is more specialized than G if F is at least as specialized as G and G is not at least as specialized as F.

But in this context, we don't care about which is more specialized. We only need P to be at least as specialized as A. All that means that deduction has to succeed from A to P. So if we synthesize some unique type U with some value V, can we deduce X<I> from X<V>? Yes. Hence, P is at least as specialized as A, so the template-argument Bar matches the template-parameter TT.


Now, passing that point, I'd say this a clang bug. The template template-parameter is template <auto>, which is what we should use to validate the expression. With a non-type template parameter auto, we'd try to use x as a value - but x isn't a valid constant expression, so this should fail. clang appears to be using template <decltype(auto) > directly - which I'm not sure is valid.

That said, I'm not sure this case has even been considered - I don't see any wording one way or the other and it's worth raising an issue.

Advantages of auto in template parameters in C++17

The template <auto> feature (P0127R1) was accepted into C++ in the ISO C++ 2016 meeting in Oulu, Finland.

An auto keyword in a template parameter can be used to indicate a non-type parameter the type of which is deduced at the point of instantiation. It helps to think of this as a more convenient way of writing:

template <typename Type, Type value>

For example,

template <typename Type, Type value> constexpr Type constant = value;
constexpr auto const IntConstant42 = constant<int, 42>;

can now be written as

template <auto value> constexpr auto constant = value;
constexpr auto const IntConstant42 = constant<42>;

where you don't need to explicitly spell out the type any more. P0127R1 also includes some simple but good examples where using template <auto> with variadic template parameters is very handy, for example for implementations of compile-time lists constant values:

template <auto ... vs> struct HeterogenousValueList {};
using MyList1 = HeterogenousValueList<42, 'X', 13u>;

template <auto v0, decltype(v0) ... vs> struct HomogenousValueList {};
using MyList2 = HomogenousValueList<1, 2, 3>;

In pre-C++1z, while HomogenousValueList could be simply written as

template <typename T, T ... vs> struct Cxx14HomogenousValueList {};
using MyList3 = Cxx14HomogenousValueList<int, 1, 2, 3>;

writing an equivalent of HeterogenousValueList would not be possible without wrapping the values in some other templates, for example:

template <typename ... ValueTypes> struct Cxx14HeterogenousValueList {};
using MyList4 = Cxx14HeterogenousValueList<constant<int, 42>,
constant<char, 'X'> >;

Is placeholder for the deduced class type of non-type template parameter a C++20 feature?

Is placeholder for the deduced class type of non-type template parameter a C++20 feature ?

Yes. [temp.param]/6 is quite clear about this:

A non-type template-parameter shall have one of the following (possibly cv-qualified) types:

  • a structural type (see below),
  • a type that contains a placeholder type ([dcl.spec.auto]), or
  • a placeholder for a deduced class type ([dcl.type.class.deduct]).

As for your concerns.

It is not well integrated in the standard, for example, the unchanged standard wording for partial ordering of function templates makes it impossible to partially specialize a class template [...]

Yes, language additions are frequently incomplete and lead to language issues. A more pressing one is the limitation on what kinds of types can be used as non-type template parameters, a limitation that prohibits std::string, std::tuple, and std::optional and the moment. These will be resolved in due time.

This syntax for placeholder inside a parameter looks like the "short concept syntax" of the concept-TS

This is just what CTAD syntax looks like.

On std-discussion, it looked like not all committee members are aware of this feature.

So?

Can auto placeholder be used to deduce function result in non-type template parameter?

Yes, auto may be used inside a compound type ([temp.param]/4.6, [dcl.type.auto.deduct]). I believe that gcc is in error in your second example: your explicitly specified T of int is substituted before performing deduction ([temp.deduct]/2.3, /5, and /6, referenced by [dcl.type.auto.deduct]/2.3 and /4).

Is there any difference between a deduced template arg and auto arg?

It is literally defined to be equivalent [dcl.fct]

An abbreviated function template is a function declaration that has one or more generic parameter type placeholders. An abbreviated function template is equivalent to a function template whose template-parameter-list includes one invented type template-parameter for each generic parameter type placeholder of the function declaration, in order of appearance.

Where generic type placeholders are the auto.

As with most equivalent syntax, it comes down to convention: pick one and stick with it.

Is there a way to pass auto as an argument in C++?

C++20 allows auto as function parameter type

This code is valid using C++20:

int function(auto data) {
// do something, there is no constraint on data
}

As an abbreviated function template.

This is a special case of a non constraining type-constraint (i.e. unconstrained auto parameter).
Using concepts, the constraining type-constraint version (i.e. constrained auto parameter) would be for example:

void function(const Sortable auto& data) {
// do something that requires data to be Sortable
// assuming there is a concept named Sortable
}

The wording in the spec, with the help of my friend Yehezkel Bernat:

9.2.8.5 Placeholder type specifiers [dcl.spec.auto]

placeholder-type-specifier:

type-constraintopt auto


type-constraintopt decltype ( auto )


  1. A placeholder-type-specifier designates a
    placeholder type that will be replaced later by deduction from an
    initializer.

  2. A placeholder-type-specifier of the form
    type-constraintopt auto can be used in the decl-specifier-seq of a
    parameter-declaration of a function declaration or lambda-expression
    and signifies that the function is an abbreviated function template
    (9.3.3.5) ...

Using auto as a template parameter

I think what you are looking for is boost::any.

std::map<std::string, boost::any> myMap;

auto is evaluated during compile time and cannot be used as a dynamic run-time type.



Related Topics



Leave a reply



Submit