A new keyword for either a type or a variable (typical use case for variadic template template)?

356 views
Skip to first unread message

Vincent Reverdy

unread,
Dec 24, 2013, 6:32:57 PM12/24/13
to std-pr...@isocpp.org
Hi,

Currently, it is impossible, as far as I know, to write a "is_specialization_of" helper struct to detect whether a type is a specialization of a given template.
It can be illustrated with the following example:
----------------------------------------------------------------------
#include <iostream>
#include <type_traits>
#include <tuple>
#include <vector>
#include <array>

template <template <class...> class Template, class T>
struct is_specialization_of
: std::false_type {};
 
template <template <class...> class Template, class... Args>
struct is_specialization_of<Template, Template<Args...>>
: std::true_type {};

int main()
{
    std::cout<<is_specialization_of<std::tuple,  std::tuple<double>>::value<<::std::endl;
    std::cout<<is_specialization_of<std::vector, std::tuple<double>>::value<<::std::endl;
    std::cout<<is_specialization_of<std::array,  std::tuple<double>>::value<<::std::endl; // Error
    return 0;
}

----------------------------------------------------------------------
It works well for templates of the form template <class...> but it fails for mixed type/variable template like std::array.
I think that it would be great (particularly for template metaprogramming) to have a a new keyword (whose name has to be defined) to declare "a type or a variable".
With this new keyword, one could type:
----------------------------------------------------------------------
#include <iostream>
#include <type_traits>
#include <tuple>
#include <vector>
#include <array>

template <template <keyword...> class Template, class T>
struct is_specialization_of
: std::false_type {};
 
template <template <keyword...> class Template, keyword... Args>
struct is_specialization_of<Template, Template<keyword...>>
: std::true_type {};

int main()
{
    std::cout<<is_specialization_of<std::tuple,  std::tuple<double>>::value<<::std::endl;
    std::cout<<is_specialization_of<std::vector, std::tuple<double>>::value<<::std::endl;
    std::cout<<is_specialization_of<std::array,  std::tuple<double>>::value<<::std::endl; // Ok
    return 0;
}

----------------------------------------------------------------------

Has it been already discussed?
If not, what is you opinion about such a thing?
If yes, what were the issues?
Do you have any suggestion for the name of this keyword?

Thank you!

Vincent R.

Vincent Reverdy

unread,
Dec 24, 2013, 6:35:41 PM12/24/13
to std-pr...@isocpp.org
Sorry, there was an error in the second example:
It is:
----------------------------------------------------------------------
template <template <keyword...> class Template, keyword... Args>
struct is_specialization_of<Template, Template<Args...>>
: std::true_type {};

----------------------------------------------------------------------
instead of:

----------------------------------------------------------------------

template <template <keyword...> class Template, keyword... Args>
struct is_specialization_of<Template, Template<keyword...>>
: std::true_type {};
----------------------------------------------------------------------

Richard Smith

unread,
Jan 6, 2014, 9:17:33 PM1/6/14
to std-pr...@isocpp.org
This seems like a useful addition to me, though perhaps we can get by without introducing a new keyword. I think there was a paper on exactly this, probably by Mike Spertus, either in 2012 or 2013, but the WG21 papers site seems to be down at the moment so I can't easily check.

One issue springs to mind: the injected-class-name of a class template can be used as a template template argument or as a type template argument:

template<template<typename> class> struct A {};
template<typename> struct B {};
template<typename T> struct C {
  A<C> a; // ok, pass template C
  B<C> b; // ok, pass type C<T>
};

I suppose we could (and probably should) preserve this behavior when 'C' is passed through such a template argument, but it's a weird corner case that you'll need to handle somehow.

Rather than introducing a new keyword, you could use a '?' token:

  template<template<? ...> class A, ? ... Ts> void f(A<Ts...>);

(or a '*' token, or something similar).

The 'auto' keyword would make sense here, but it might make more sense being restricted to a non-type template parameter (with its type deduced from the template argument). Perhaps it could be made to fill both roles:

  template<auto X> void f() {
    int a = X; // it's a value by default
  }
  template<auto X> void g() {
    typename X y; // it can be explicitly treated as a type
  }
  template<auto X> void h() {
    template X<int> z; // or as a template
  }
  template<auto &X> void i(); // not exactly 'auto', always a non-type template parameter

... though this would require a lot of hacking with the grammar, to allow 'template' and 'typename' in these new places.

David Krauss

unread,
Jan 6, 2014, 10:31:18 PM1/6/14
to std-pr...@isocpp.org
On 1/7/14 10:17 AM, Richard Smith wrote:
> Rather than introducing a new keyword, you could use a '?' token:
>
> template<template<? ...> class A, ? ... Ts> void f(A<Ts...>);
>
> (or a '*' token, or something similar).

This idea is unscalable if the ? represents a particular template
parameter list. If there were two template template parameters with
different lists, you would end up attempting to assign both to ? .

I'm pretty sure I wrote something about the issue on this board a while
ago, but can't recall the details.

A bare ellipsis seems to make it clearer that there is no underlying
storage or meta-variable.

template<template<...> class A, ... Ts> void f(A<Ts...>);


In this case, the template parameter list of A and the argument
types/kinds of Ts are deduced separately, and conversion potentially
occurs when forming A<Ts...> . No expressiveness is lost versus the "?"
syntax because names at the call site are determined to refer to types
or objects independently of the template parameter list, so ? would not
pass any information back to the user anyway.

> The 'auto' keyword would make sense here, but it might make more sense
> being restricted to a non-type template parameter (with its type deduced
> from the template argument). Perhaps it could be made to fill both roles:

As for the non-type role, that sounds nice as a separate proposal. The
standard explicitly mentions that it's impossible to pass a non-type
argument and deduce its type, and it makes usage of e.g.
std::integral_constant repetitive.

>
> template<auto X> void f() {
> int a = X; // it's a value by default
> }
> template<auto X> void g() {
> typename X y; // it can be explicitly treated as a type
> }
> template<auto X> void h() {
> template X<int> z; // or as a template
> }
> template<auto &X> void i(); // not exactly 'auto', always a non-type
> template parameter
>
> ... though this would require a lot of hacking with the grammar, to allow
> 'template' and 'typename' in these new places.

Looks like a solution in search of a problem. Every use of the parameter
would have to be disambiguated, and since the parameter can only refer
to the typename or object passed by the caller, each disambiguation
would have to go the same way. Might as well keep the status quo.

hun.nem...@gmail.com

unread,
Jan 7, 2014, 1:55:40 AM1/7/14
to std-pr...@isocpp.org
I think compile time reflection can handle this case.
Based on this paper (https://github.com/hun-nemethpeter/cpp-reflector-mini/blob/master/Proposal.md),
the result will be similar to this:


// driver
struct SpecializationDriver
{
   
constexpr SpecializationDriver(const meta::template_template_decl& templateTemplateDecl,
                                   
const meta::template_decl& templateDecl)
     
: result(templateTemplateDecl.getName() == templateDecl.getName()) { }
   
constexpr bool result;
};

template<template <class...> class Template, class T> $use(SpecializationDriver driver)
bool is_specialization_of()
{
   
return driver.result;
}

int main()
{
    std
::cout<< is_specialization_of<std::tuple,  std::tuple<double>>() <<::std::endl;
    std
::cout<< is_specialization_of<std::vector, std::tuple<double>>() <<::std::endl;
    std
::cout<< is_specialization_of<std::array,  std::tuple<double>>() <<::std::endl;
   
return 0;
}


Peter

inkwizyt...@gmail.com

unread,
Jan 7, 2014, 3:05:33 PM1/7/14
to std-pr...@isocpp.org, hun.nem...@gmail.com
How it would help? `template <class...> class Template` still cant accept `std::array<class,int>`.

hun.nem...@gmail.com

unread,
Jan 8, 2014, 12:56:17 AM1/8/14
to std-pr...@isocpp.org, hun.nem...@gmail.com, inkwizyt...@gmail.com
You are right, it will not help. I didn't realized that template <class...> class Template` cant accept `std::array<class,int>.
I thought that the problem occurs in Template<Args...>.

Peter

Richard Smith

unread,
Jan 8, 2014, 4:24:53 PM1/8/14
to std-pr...@isocpp.org
On Mon, Jan 6, 2014 at 7:31 PM, David Krauss <pot...@gmail.com> wrote:
On 1/7/14 10:17 AM, Richard Smith wrote:
Rather than introducing a new keyword, you could use a '?' token:

   template<template<? ...> class A, ? ... Ts> void f(A<Ts...>);

(or a '*' token, or something similar).

This idea is unscalable if the ? represents a particular template parameter list. If there were two template template parameters with different lists, you would end up attempting to assign both to ? .

That's not the idea. ? doesn't get assigned a value, it just means "any type, non-type, or template template argument is OK here", exactly as the original poster requested. So <? ...> is exactly equivalent to your suggested <...>.
 
I'm pretty sure I wrote something about the issue on this board a while ago, but can't recall the details.

A bare ellipsis seems to make it clearer that there is no underlying storage or meta-variable.

template<template<...> class A, ... Ts> void f(A<Ts...>);

This does not seem like a complete solution, because it does not provide a way to have a *single* template parameter of any kind. That in turn can be useful if generic code wants to process an arbitrary list of template arguments in some way:

  template<? ...> struct do_stuff { using type = void; };
  template<? X, ? ...Xs> struct do_stuff<X, Xs...> { using type = foo<bar<X>, typename do_stuff<Xs...>::type>; };

In this case, the template parameter list of A and the argument types/kinds of Ts are deduced separately, and conversion potentially occurs when forming A<Ts...> . No expressiveness is lost versus the "?" syntax because names at the call site are determined to refer to types or objects independently of the template parameter list, so ? would not pass any information back to the user anyway.


The 'auto' keyword would make sense here, but it might make more sense
being restricted to a non-type template parameter (with its type deduced
from the template argument). Perhaps it could be made to fill both roles:

As for the non-type role, that sounds nice as a separate proposal. The standard explicitly mentions that it's impossible to pass a non-type argument and deduce its type, and it makes usage of e.g. std::integral_constant repetitive.

Yes, there's probably an argument for having both, even though there's a lot of overlap between them.
 

   template<auto X> void f() {
     int a = X; // it's a value by default
   }
   template<auto X> void g() {
     typename X y; // it can be explicitly treated as a type
   }
   template<auto X> void h() {
     template X<int> z; // or as a template
   }
   template<auto &X> void i(); // not exactly 'auto', always a non-type
template parameter

... though this would require a lot of hacking with the grammar, to allow
'template' and 'typename' in these new places.

Looks like a solution in search of a problem. Every use of the parameter would have to be disambiguated, and since the parameter can only refer to the typename or object passed by the caller, each disambiguation would have to go the same way. Might as well keep the status quo.

Uses as a non-type parameter would not need disambiguation, and it solves both the problem originally raised in this thread and the more-constrained problem raised in the "T for two" section of N3405 and the resulting N3601. But I agree that separate syntax for the two problems is probably the better choice -- I was just pointing out that it's not the only option.

Bengt Gustafsson

unread,
Jan 8, 2014, 6:08:46 PM1/8/14
to std-pr...@isocpp.org
I agree that something like your ? is needed to be able to disassemble the list of template arguments.

We would also need some way to overload based on whether the "head", i.e. your ?X is a type or a value. Maybe this can be done with partial class template specialization already with the aid of SFINAE but what we need to boil it down to is:

is_type<?X>::value to be true or false depending on if X is a type or a value.

If it is not a type we also need to know the type of the value, which I imagine can be done with decltype(X) iff X is not a type.

To make sure that we don't use this decltype erroneously we would have to specialize helper templates on is_type<?X>::value in the regular template programming style unless we get a static if so that a more imperative style can be used.


Here is a try at implementing is_type:

// Base declaration;
template<?X> struct is_type;

// Specialization for typename:
template<> struct is_type<typename X> { static const bool value = true; }
template<typename T> is_type<T X> { static const bool value = false; }

Does this make any sense? I'm very uncertain about the empty <> in the first specialization, is it logical at all?

Whatever solution can be found to this detail it is important that it can be done, it is nice if it can be done without introducing any more language features than the ? itself. It is also good if such a trait would be added to the standard library to avoid having to invent it over and over.

Richard Smith

unread,
Jan 8, 2014, 7:44:43 PM1/8/14
to std-pr...@isocpp.org
On Wed, Jan 8, 2014 at 3:08 PM, Bengt Gustafsson <bengt.gu...@beamways.com> wrote:
I agree that something like your ? is needed to be able to disassemble the list of template arguments.

We would also need some way to overload based on whether the "head", i.e. your ?X is a type or a value. Maybe this can be done with partial class template specialization already with the aid of SFINAE but what we need to boil it down to is:

is_type<?X>::value to be true or false depending on if X is a type or a value.

If it is not a type we also need to know the type of the value, which I imagine can be done with decltype(X) iff X is not a type.

Here's an implementation:

  template<? X> struct is_type : std::false_type {};
  template<typename T> struct is_type<T> : std::true_type {};

(and likewise we can implement is_template and is_nontype).

To make sure that we don't use this decltype erroneously we would have to specialize helper templates on is_type<?X>::value in the regular template programming style unless we get a static if so that a more imperative style can be used.


Here is a try at implementing is_type:

// Base declaration;
template<?X> struct is_type;

// Specialization for typename:
template<> struct is_type<typename X> { static const bool value = true; }
template<typename T> is_type<T X> { static const bool value = false; }

Does this make any sense? I'm very uncertain about the empty <> in the first specialization, is it logical at all?

The <> doesn't make sense here; that'd be the syntax for an explicit specialization, which is not what you want here.
 
Whatever solution can be found to this detail it is important that it can be done, it is nice if it can be done without introducing any more language features than the ? itself. It is also good if such a trait would be added to the standard library to avoid having to invent it over and over.

Yes, I think the ? extension alone is sufficient for this.

With the addition of some motivating examples, this seems ready to be written up as a paper to me. I'm somewhat optimistic that the most contentious part of this will be the syntax ('?' or 'auto' or a new keyword or something else).
 
--
 
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

David Krauss

unread,
Jan 8, 2014, 8:28:13 PM1/8/14
to std-pr...@isocpp.org
On 1/9/14 8:44 AM, Richard Smith wrote:
> Here's an implementation:
>
> template<? X> struct is_type : std::false_type {};
> template<typename T> struct is_type<T> : std::true_type {};

This appears to be using ? like auto, not to mean "type or object."

Also, as you know, class templates cannot currently be overloaded like
this. Functions can be, so:

template< typename >
std::true_type is_type();

template< int > // Insufficient generality, to be fixed by "auto"-like
type deduction.
std::false_type is_type();

template< template< typename ... > class > // Insufficient generality,
to be fixed by "?"-like kind deduction.
std::false_type is_type();

bool three_is_type = decltype( is_type< 3 >() )::value,
double_is_type = decltype( is_type< double >() )::value,
vector_is_type = decltype( is_type< std::vector >() )::value;

> Yes, I think the ? extension alone is sufficient for this.
>
> With the addition of some motivating examples, this seems ready to be
> written up as a paper to me. I'm somewhat optimistic that the most
> contentious part of this will be the syntax ('?' or 'auto' or a new keyword
> or something else).

I still don't see the value of an identifier, as opposed to a pack
expansion element, that can be either a type or a value. And such a name
would throw a wrench into template parsing. They could *only* be used as
explicit template arguments to functions as in my example above, or to
template template parameters with "?"-like parameter lists.

If you end up needing to use is_type, you're already in deep shit.

On the other hand, parameter lists are already unpacked using
overloading, or partial specialization which works the same way. This
gives you a branching point to different code paths for type, value, or
template, which can already be written and parsed without redesigning
dependent name lookup.

This is why I think "..." is sufficient with no motivation for "?" or
any provision for a single argument.

Richard Smith

unread,
Jan 8, 2014, 9:41:54 PM1/8/14
to std-pr...@isocpp.org
On Wed Jan 08 2014 at 5:28:21 PM, David Krauss <pot...@gmail.com> wrote:
On 1/9/14 8:44 AM, Richard Smith wrote:
> Here's an implementation:
>
>    template<? X> struct is_type : std::false_type {};
>    template<typename T> struct is_type<T> : std::true_type {};

This appears to be using ? like auto, not to mean "type or object."

No, it's using it to mean "type or object".
 
Also, as you know, class templates cannot currently be overloaded like
this.

It's not an overload, it's a partial specialization.
 
--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

David Krauss

unread,
Jan 8, 2014, 9:54:12 PM1/8/14
to std-pr...@isocpp.org
On 1/9/14 10:41 AM, Richard Smith wrote:
> On Wed Jan 08 2014 at 5:28:21 PM, David Krauss <pot...@gmail.com> wrote:
>
> It's not an overload, it's a partial specialization.

OK, that wasn't clear from the example. So do you intend that an
identifier declared with "?" never be used? What could you possibly do
with X?

Quoting the previous message:

>> I still don't see the value of an identifier, as opposed to a pack
>> expansion element, that can be either a type or a value. And such a name
>> would throw a wrench into template parsing. They could *only* be used as
>> explicit template arguments to functions as in my example above, or to
>> template template parameters with "?"-like parameter lists.

Edit: You couldn't even use X in these cases, because parse time
ambiguity of a template argument is always resolved in favor of a
typename ([temp.arg] §14.3/2).

Richard Smith

unread,
Jan 8, 2014, 10:18:08 PM1/8/14
to std-pr...@isocpp.org
On Wed, Jan 8, 2014 at 6:54 PM, David Krauss <pot...@gmail.com> wrote:
On 1/9/14 10:41 AM, Richard Smith wrote:
On Wed Jan 08 2014 at 5:28:21 PM, David Krauss <pot...@gmail.com> wrote:

It's not an overload, it's a partial specialization.

OK, that wasn't clear from the example. So do you intend that an identifier declared with "?" never be used? What could you possibly do with X?

You would only be able to use it as a template-argument.
 
Quoting the previous message:

I still don't see the value of an identifier, as opposed to a pack
expansion element, that can be either a type or a value. And such a name
would throw a wrench into template parsing. They could *only* be used as
explicit template arguments to functions as in my example above, or to
template template parameters with "?"-like parameter lists.

Edit: You couldn't even use X in these cases, because parse time ambiguity of a template argument is always resolved in favor of a typename ([temp.arg] §14.3/2).

The useful thing about introducing a new feature is that you get to specify how it works =) Naturally, we would specify that passing a generic template argument to another template preserves the kind of template argument.
 
If you end up needing to use is_type, you're already in deep shit.

On the other hand, parameter lists are already unpacked using
overloading, or partial specialization which works the same way. This
gives you a branching point to different code paths for type, value, or
template, which can already be written and parsed without redesigning
dependent name lookup.

This is why I think "..." is sufficient with no motivation for "?" or
any provision for a single argument.


David Krauss

unread,
Jan 9, 2014, 7:11:28 PM1/9/14
to std-pr...@isocpp.org
On 1/9/14 11:18 AM, Richard Smith wrote:
> The useful thing about introducing a new feature is that you get to specify
> how it works =) Naturally, we would specify that passing a generic template
> argument to another template preserves the kind of template argument.

But how can you make that work without redoing how parsers must classify
type-dependent expressions? And what's the benefit, given that
overloading/partial specialization already solve the problem?

David Krauss

unread,
Jan 9, 2014, 7:23:28 PM1/9/14
to std-pr...@isocpp.org
I should be more specific.

Given "?", you can peel off a fully-generic argument from a pack, and
pass it to another template. But the other template must be either
fully-generic, or overloaded/partially specialized. So you might as well
peel the pack in the target overloaded/partially specialized template.
All "?" buys you is the freedom to add a dispatcher between components
which are actually necessary.

I don't see any applications for fully-generic arguments outside
metaprocessing, e.g. of packs. There is no runtime code application
parameterized over all types or all values of any type. For value-like
types, i.e. std::integral_constant, it is idiomatic (and makes perfect
sense) to deal with runtime instances of a stateless class.

Richard Smith

unread,
Jan 9, 2014, 8:07:21 PM1/9/14
to std-pr...@isocpp.org
On Thu, Jan 9, 2014 at 4:23 PM, David Krauss <pot...@gmail.com> wrote:
On 1/10/14 8:11 AM, David Krauss wrote:
On 1/9/14 11:18 AM, Richard Smith wrote:
The useful thing about introducing a new feature is that you get to specify
how it works =) Naturally, we would specify that passing a generic template
argument to another template preserves the kind of template argument.

But how can you make that work without redoing how parsers must classify type-dependent expressions? And what's the benefit, given that overloading/partial specialization already solve the problem?

I should be more specific.

Given "?", you can peel off a fully-generic argument from a pack, and pass it to another template. But the other template must be either fully-generic, or overloaded/partially specialized. So you might as well peel the pack in the target overloaded/partially specialized template. All "?" buys you is the freedom to add a dispatcher between components which are actually necessary.

It allows more code reuse, by allowing handling of individual elements of such a pack to be factored out. That is a valuable property, even if it only helps those people who are writing template metaprograms.

David Krauss

unread,
Jan 9, 2014, 8:32:48 PM1/9/14
to std-pr...@isocpp.org
On 1/10/14 9:07 AM, Richard Smith wrote:
> On Thu, Jan 9, 2014 at 4:23 PM, David Krauss <pot...@gmail.com> wrote:
>
>> On 1/10/14 8:11 AM, David Krauss wrote:
>>
>> Given "?", you can peel off a fully-generic argument from a pack, and
>> pass it to another template. But the other template must be either
>> fully-generic, or overloaded/partially specialized. So you might as
>> well peel the pack in the target overloaded/partially specialized
>> template. All "?" buys you is the freedom to add a dispatcher between
>> components which are actually necessary.
> It allows more code reuse, by allowing handling of individual elements of
> such a pack to be factored out. That is a valuable property, even if it
> only helps those people who are writing template metaprograms.

I'm still skeptical that real factoring is ever possible, i.e. that a
template with a fully generic non-pack parameter, and doing more than
forward it (which could be done by a tuple-like, pack-based utility),
can ever usefully be more than a simple dispatcher. This refers to the
primary template, not the specializations. Your previous example use
case did not actually use the fully-generic single argument at all, and
might as well have taken a pack:

template< ... > struct is_type : std::false_type {}; // primary
template<typename T> struct is_type<T> : std::true_type {}; // partial specialization


This is assuming, as the previous example already did, that "typename"
is more specialized than fully-generic. Presumably "<typename>" is still
never more specialized than "<typename ...>". We can remove that
assumption, and contain the pattern making it scalable by adding that
tuple-like utility:

template< ... > struct generic_tuple; // library utility

template< typename > struct is_type : std::false_type {}; // primary
template<typename T> struct is_type< generic_tuple< T > > : std::true_type {}; // partial specialization


The user would have no need for an is_type facility, though, because
peeling a tuple element would always require type/value resolution.

Prototyping and practice will tell. But, let's keep an eye out for
unnecessary complexity masquerading as factoring.

Bengt Gustafsson

unread,
Jan 10, 2014, 5:01:31 AM1/10/14
to std-pr...@isocpp.org
Except that it is error prone I like your idea:

   template< ... > struct is_type : std::false_type {}; // primary 
   template<typename T> struct is_type<T> : std::true_type {}; // partial specialization 

(error prone in the sense that if you call it with more than one type it tells you it isn't a type).

But in our use case we would first have to split the incoming generic pack into its individual elements to be able to test them for type/non-type. Can this be done with only the ... feature. I think it can, but only given the other "auto" feature. I exemplify by a simple function to get info about the first element of a generic pack:

   template<...> struct HeadInfo;
   template<typename T, ...> struct HeadInfo<T, ...> {
       static const bool is_type = true;
       typedef T type;
   };
   template<auto V, ...> struct HeadInfo<V, ...> {
       static const bool is_type = false;
       typedef decltype(V) type;
       static const auto value = V;
   };

Well, now that I wrote the code I noticed that the same "default based" technique used for is_type is usable here too, except of course that you would not get the type and value members for the non typename case. Also, it seems that it gets hard to work with the "always variadic" nature of the resulting templates, for instance we could not write static const bool is_type = std::is_type<...> here as it would be exposed to the error-proneness I described at the top of this post.

My conclusion for now is that if we have 'auto' as I used it above we only need the ... notation for variadic generic parameters, as auto and typename can be used to differentiate the members of the generic pack as shown in the HeadInfo example. Thus a new implementation of is_type is like this:

 template< ... > struct is_type; // primary, not implemented. This prevents using is_type with more than one element packs.
 template<auto V> : struct is_type<V> : std::false_type {};

 template<typename T> struct is_type<T> : std::true_type {}; // partial specialization 

The only problem I see with this is that it is kind of weird to define the primary template with ... which denotes any number of anything. At least error messages when you instantiate with more than one parameter will be misleading, as it will complain about the class being incomplete rather than the template parameters being too many. This argument alone seems however to be too weak to motivate a ? feature.

Alex B

unread,
Jan 10, 2014, 10:35:15 AM1/10/14
to std-pr...@isocpp.org
On Friday, January 10, 2014 5:01:31 AM UTC-5, Bengt Gustafsson wrote:
 
   template<...> struct HeadInfo;
   template<typename T, ...> struct HeadInfo<T, ...> {
       static const bool is_type = true;
       typedef T type;
   };
   template<auto V, ...> struct HeadInfo<V, ...> {
       static const bool is_type = false;
       typedef decltype(V) type;
       static const auto value = V;
   };
 
 
That syntax without ? would still require being able to name the generic pack. Your example should be:
 
template<typename T, ...Tail> struct HeadInfo<T, Tail...> {
static const bool is_type = true;
typedef T type;
};
 
 
Giving a name would be required to disambiguate cases where there is more than one generic pack:
 
template <...> struct A {};
 
template <class T, class U>
struct B;
 
template <...ElemsT, ...ElemsU>
struct B<A<ElemsT...>, A<ElemsU...>>
{
};
 
As for actual use cases (with or without ?), there is more than metaprogramming refactoring sugar. I can see at least one case that it would allow to fix which is boost recursive variants:
Unless I'm wrong, recursive variants currently cannot contain a template type with non-type parameters, which is a bit annoying. This feature would make it possible.

Bengt Gustafsson

unread,
Jan 10, 2014, 6:41:39 PM1/10/14
to std-pr...@isocpp.org
Yes, you are absolutely right, the "Tail" should have been there in my example just as you wrote it.

So it seems right now that we can make do with the ... "generic pack" type and auto, but it bugs me a bit that we now have all of these forms of a template parameter type (where int is of course just an example):

int                  One template parameter of type int
int...               Any number of int template parameters
auto               One non-type template parameter of any type
auto...            Any number of non-type template parameters of mixed types*
typename       One type template parameter
typename...    Any number of type template parameters
...                  Any number of type or non-type template parameters

But not:
?                   One type or auto tempalte parameter  (apart from the bikeshed issue of ?)


At least from an educational standpoint it could be wise to be more orthogonal, which would also mean to reintroduce ?... instead of just ...

Another risk I see with using ... is that people will start to over-use it because it looks nice, i.e. writing template<...> for a variadic template template parameter even when the code only handles that the actuals are types.

In the back of my head I also feel that sooner of later we will find something that can't be done due to the omission of the lonely ? feature.

Entering the bikeshed I toyed with the use of . instead of ? It kind of goes nicely with ... as it would be interpreted as "many dots". The main problem could be that it is almost invisible on the screen especially as a typical usage would be template<. H,... T>. The * suggested in this thread is a bit scary as you can also write for instance template<int*...> which as another traditional meaning...  I also scanned the keyword list but didn't find any real candidates.

BTW: I can't seem to find any wording in the standard document (C++14) that allows for instance a function taking any number of ints, although I have seen uses of it in this list (and which I assumed was available in my list above):

template<int... dims> class Matrix; 

VS2012 also does not allow this. What is the status? 

* The reason for assuming that auto... would refer to any number of parameters of different types is that otherwise reursive decomposition like this would be strange:

template<auto H, auto... Ts> class Foo;

as now H is "another auto" than Ts which can bind to another type, while the Ts must still be the same, untill next recursion step.

Is there a paper or thread for auto template parameters?

Richard Smith

unread,
Jan 10, 2014, 7:43:25 PM1/10/14
to std-pr...@isocpp.org
On Fri Jan 10 2014 at 3:41:44 PM, Bengt Gustafsson <bengt.gu...@beamways.com> wrote:
Yes, you are absolutely right, the "Tail" should have been there in my example just as you wrote it.

So it seems right now that we can make do with the ... "generic pack" type and auto, but it bugs me a bit that we now have all of these forms of a template parameter type (where int is of course just an example):

int                  One template parameter of type int
int...               Any number of int template parameters
auto               One non-type template parameter of any type
auto...            Any number of non-type template parameters of mixed types*
typename       One type template parameter
typename...    Any number of type template parameters

Also:

template<template-parameter-list> class
template<template-parameter-list> class...
 
...                  Any number of type or non-type template parameters

But not:
?                   One type or auto tempalte parameter  (apart from the bikeshed issue of ?)


At least from an educational standpoint it could be wise to be more orthogonal, which would also mean to reintroduce ?... instead of just ...

Another risk I see with using ... is that people will start to over-use it because it looks nice, i.e. writing template<...> for a variadic template template parameter even when the code only handles that the actuals are types.

In the back of my head I also feel that sooner of later we will find something that can't be done due to the omission of the lonely ? feature.

Entering the bikeshed I toyed with the use of . instead of ? It kind of goes nicely with ... as it would be interpreted as "many dots". The main problem could be that it is almost invisible on the screen especially as a typical usage would be template<. H,... T>. The * suggested in this thread is a bit scary as you can also write for instance template<int*...> which as another traditional meaning...  I also scanned the keyword list but didn't find any real candidates.

Yes, I also scanned the keyword list before suggesting '?'. The only plausible candidate there was 'auto', but that makes more sense as a generic non-type template parameter. '?' doesn't seem like a great name to me, but it at least gives us something to talk about for now. Hopefully someone can find something better. '.' versus '...' seems cute, but perhaps it's too cute, and as you say, the dot is easily missed.

BTW: I can't seem to find any wording in the standard document (C++14) that allows for instance a function taking any number of ints, although I have seen uses of it in this list (and which I assumed was available in my list above):

template<int... dims> class Matrix; 

VS2012 also does not allow this. What is the status? 

This is valid in C++11. We have:

14.1/1:
  template-parameter:
    type-parameter
    parameter-declaration

Per 8.3.5, 'int... dims' is a parameter-declaration. Per 14.1/15, such a template-parameter declares a parameter pack.
 
* The reason for assuming that auto... would refer to any number of parameters of different types is that otherwise reursive decomposition like this would be strange:

template<auto H, auto... Ts> class Foo;

as now H is "another auto" than Ts which can bind to another type, while the Ts must still be the same, untill next recursion step.

Is there a paper or thread for auto template parameters?

No, it came up during discussion of N3601 at the Bristol WG21 meeting, but I don't believe there's any paper on it. (And at Bristol, EWG was pretty evenly divided between wanting N3601's syntax and wanting 'auto'.)
 
Den fredagen den 10:e januari 2014 kl. 16:35:15 UTC+1 skrev Alex B:
On Friday, January 10, 2014 5:01:31 AM UTC-5, Bengt Gustafsson wrote:
 
   template<...> struct HeadInfo;
   template<typename T, ...> struct HeadInfo<T, ...> {
       static const bool is_type = true;
       typedef T type;
   };
   template<auto V, ...> struct HeadInfo<V, ...> {
       static const bool is_type = false;
       typedef decltype(V) type;
       static const auto value = V;
   };
 
 
That syntax without ? would still require being able to name the generic pack. Your example should be:
 
template<typename T, ...Tail> struct HeadInfo<T, Tail...> {
static const bool is_type = true;
typedef T type;
};
 
 
Giving a name would be required to disambiguate cases where there is more than one generic pack:
 
template <...> struct A {};
 
template <class T, class U>
struct B;
 
template <...ElemsT, ...ElemsU>
struct B<A<ElemsT...>, A<ElemsU...>>
{
};
 
As for actual use cases (with or without ?), there is more than metaprogramming refactoring sugar. I can see at least one case that it would allow to fix which is boost recursive variants:
Unless I'm wrong, recursive variants currently cannot contain a template type with non-type parameters, which is a bit annoying. This feature would make it possible.

--
 
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
Reply all
Reply to author
Forward
0 new messages