Why can't new template parameters be introduced into a full specialization?

In Where in the standard C ++ 11 is prohibited "template <typename T> class A {...}; template <typename T> class A <int> {...}; " (if anywhere)? , the following syntax has been confirmed to be prohibited in the C ++ 11 standard:

/* invalid C++ */

template <typename T>
class A
{
    public:
    T t;
};

// phony "full specialization" that mistakenly attempts
// to introduce a *new* template parameter
template <typename T>
class A<int>
{
    public:
    int i;
    T t;
};

      

With a full understanding that the above syntax does not represent valid C ++, I could still envision a syntactically unambiguous use of the above code snippet like this:

A<float> a1;
A<int><double> a2;

a1.t = 2.0f;
a2.i = 2;
a2.t = 2.0;

      

It would seem syntactically and semantically unambiguous for C ++ to support the syntax above.

(If the intended semantics are not clear to anyone, post a comment and I will explain.)

I would describe this syntax as "introducing a new template parameter into a full specialization".

In this imaginary C ++ compiler script, modified to support the above syntax and semantics, the compiler will see A<int> a2;

and know that the instantiation attempt matches the main pattern; he will then seek specializations and find and select a full specialization A<int>

(ignoring <double>

); he would note that this full specialization introduces a new template parameter T

, which in the case of a declared variable a2

is double

.

If I am correct that the above syntax and semantics are unambiguous, then I would like to know why this is not part of C ++. I can think of three reasons, but perhaps the answer is different or more complex.

  • There are (almost) no real-world scenarios where this would be useful
  • It is too complex for the current compilers to be proposed to support
  • Too many potential conflicts with other language features, which would require many new rules.
  • Actually, the fourth possibility is that the above syntax and semantics would be useful in some real-world scenarios and could reasonably be included in the standard, but this is simply not part of the current standard.

I would like to know why this is not supported in C ++. Is it correct that one of my points indicates the answer?

+3


source to share


4 answers


Oooh, this explodes in funny ways.

For the sake of clarity, I will assume that you mean that with your example code

 A<double> a; // okay
 A<int> a2;   // not okay, A<int> is not a class but a class template.

      

Now try using this in other templates. Consider

template<typename T>
void function<A<T> const &a) { ... }

A<double> a;
A<int><double> a2;

function(a);  // that looks okay.
function(a2); // er...compiler error, I guess?

      

It's not so bad; the compiler might just complain about it. I think we're starting to see how strange this is. Well, take it up a notch:

template<template<typename> class A> struct Foo { ... };

Foo<A> f; // this would usually work, does it now?

      

If your answer is no, what if the specialization is not known at the point of compilation?



EDIT: Expanding this a bit, consider a real world scenario for template-template arguments, which I like to call package properties:

template<template<typename> class, typename...> struct applies_to_all;

template<template<typename> class predicate, typename T, typename... Pack>
struct applies_to_all<predicate, T, Pack...> {
  static bool const value =
    predicate<T>::value && applies_to_all<predicate, Pack...>::value;
};

template<template<typename> class predicate>
struct applies_to_all<predicate> {
  static bool const value = true;
};

...

bool b = applies_to_all<std::is_integral, int, long, double>::value;

      

And write it like A

that class for everything except int

, and class template for int

. Then try doing

applies_to_all<A, double, float, std::string, int>::value

      

And note that this simple case is not the code you are likely to see. What you really get is stuff nested three levels deep in the other templates, where it looks like this:

applies_to_all<A, T...>::value

      

Perhaps behavior could be defined for this that is mathematically consistent, but I doubt it would be possible to define what is useful. Of course it is not POLA compliant.

You can probably find much more in these lines. Blurring the lines between classes and class templates in this way inevitably destroys all sorts of things.

+5


source


A name in C ++ represents one kind of entity, and this kind is known on the first lookup of the name.

The name A

in your example is the name of the class template. It follows that A<T>

for any T

is the name of the class.

This is a useful property for the language. Violation of this property means that the language takes a serious hit in complexity (for both the compiler and the user).



So even if you could find all the edge cases and fix the problems Wintermute talked about, it would still likely get rejected because it makes the language more irregular, more awesome, less uniform. C ++ is already burdened with quite a bit of heterogeneity; there is no point in adding more.

I would encourage you to post your intended use case and we might show you a better way.

+3


source


The template resolution is always against the main template.

Any additional template parameters introduced into the specialization will just hang in the air, and nothing will tie them to using any template.

+1


source


One of the main semantics of templates in C ++ is that the identity of the template has a single template-argument-list: A<int>

. The proposed modification allows the template ID to have more than one list template-arguments A<int><double>

.

You can achieve the desired result in standard C ++ using template default arguments:

template <typename T, typename U = void>
struct A
{
    T t;
};

template <typename U>
struct A<int, U>
{
    int i;
    T t;
};

A<float> a1;
A<int, double> a2;

a1.t = 2.0f;
a2.i = 2;
a2.t = 2.0;

      

The syntax is A<int, double>

simpler than A<int><double>

- a single argument list rather than multiple argument lists, which is the main reason I would not make the proposed change.

Having said that, one could remove the requirement that all explicit / partial template specializations take the same number of template arguments. Instead, you might want the specialization to accept at least as many template arguments as the main template. If so, you can omit the fallback second parameter in the main template.

template <typename T>
struct A; // primary template, definition omitted for brevity

template <typename U>
struct A<int, U>; // partial specialization, definition omitted for brevity

A<float> a1; // OK, selects primary template
A<int, double> a2; // OK, selects partial specialization
A<float, float> a3; // Error, no valid specialization

      

I don't think this change will break anything. The next question is: will it improve anything?

+1


source







All Articles