Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Consider a simple example:

int x;

template <template <auto> class TT>
struct Foo {
   void foo() {
      TT<(x)> tt;
      static_cast<void>(tt);
   }
};

template <decltype(auto)>
struct Bar { };


int main() {
    Foo<Bar> foobar;
    foobar.foo();
}

[clang] seems to deal with the idea of decltype(auto) placeholder despite the use of auto in template template parameter declaration without a problem.

[gcc] on the other hand - not very well:

prog.cc:6:13: error: the value of 'x' is not usable in a constant expression


As usually - which behaviour is expected according to standard? Or maybe everything is possible and the code is ill-formed (this time I suppose not but cannot rule it out definitively)?

PS. Sorry for breaking one of the compilers again ;)

like image 223
W.F. Avatar asked Sep 19 '17 20:09

W.F.


People also ask

Which parameter is allowed for non-type template?

Which parameter is legal for non-type template? Explanation: The following are legal for non-type template parameters:integral or enumeration type, Pointer to object or pointer to function, Reference to object or reference to function, Pointer to member.

Can we use non-type parameters as argument templates?

A non-type template argument provided within a template argument list is an expression whose value can be determined at compile time. Such arguments must be constant expressions, addresses of functions or objects with external linkage, or addresses of static class members.

Which is the correct example of template parameters?

For example, given a specialization Stack<int>, “int” is a template argument. Instantiation: This is when the compiler generates a regular class, method, or function by substituting each of the template's parameters with a concrete type.

Can a template be a template parameter?

Templates can be template parameters. In this case, they are called template parameters. The container adaptors std::stack, std::queue, and std::priority_queue use per default a std::deque to hold their arguments, but you can use a different container.


1 Answers

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.

like image 154
Barry Avatar answered Nov 15 '22 17:11

Barry