16

When CRTP is used inside a template, (or generally when a template parameter is passed as a base class template argument), is it impossible to name the base's member templates in a using declaration?

template< typename d >
struct base {
    template< typename >
    struct ct {};

    template< typename >
    void ft() {}
};

template< typename x >
struct derived : base< derived< x > > {
     using derived::base::template ct; // doesn't work
     using derived::base::ft; // works but can't be used in a template-id
};

It seems to me that this is a hole in the language, simply because the using-declaration grammar production doesn't incorporate a qualified-id.

using-declaration:
    using typename(opt) nested-name-specifier unqualified-id ; // have this
    using :: unqualified-id ;

unqualified-id:
    identifier
    operator-function-id
    conversion-function-id
    literal-operator-id
    ~ class-name
    ~ decltype-specifier
    template-id

qualified-id:
    nested-name-specifier template(opt) unqualified-id // want this
    :: identifier
    :: operator-function-id
    :: literal-operator-id
    :: template-id

If the only rule were using-declaration: using typename(opt) qualified-id, the only consequences would be

  • ruling out :: conversion-function-id, :: ~ class-name, and :: ~ decltype-specifier template-id which make no semantic sense,
  • allowing :: template-id which is already expressly forbidden by 7.3.3/5, and
  • allowing the template keyword which already has sufficient specification to patch the hole.

Is this analysis correct?

Given that the new grammar were allowed, perhaps a declaration with typename should import a class template or alias template, and one without typename should import a function or variable template into the current scope.

     using typename derived::base::template ct;
     using derived::base::ft;

This might require some additional specification. Also, the current status quo seems to be that dependent template-names always have ambiguous kind (not template-ids), so it's not clear that typename belongs with ct at all.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • An interesting analysis. I'm curious where you would *use* this in such a fashion that the identified "hole" would be inhibiting. I.e. what is a real-world application where this is the roadblock that causes you to have to step back and reconsider the architecture of the program? – WhozCraig Mar 15 '13 at 02:19
  • @WhozCraig This comes up any time you use CRTP from a template. The base class provides an interface for which you must say `this->*template` or `base::template` at every use. – Potatoswatter Mar 15 '13 at 02:20
  • Ok, I think I get it. This particular case is in regards to using that with a `using` clause to make life easier, but it doesn't seem to be allowed. Good example. Thank you. – WhozCraig Mar 15 '13 at 02:24
  • Not sure what the question really is... are you asking about the language or are you asking whether that hypothetical change in the language would make sense/be correct/solve your problem? – David Rodríguez - dribeas Mar 15 '13 at 03:52
  • @DavidRodríguez-dribeas Both. If I'm wrong about the language, then I'm certainly wrong about how to fix it. I was sort of hoping I was wrong about the language but now that seems a slim chance. Maybe at this point I should proceed to ask whether this should be a DR or a proposal paper… – Potatoswatter Mar 15 '13 at 07:28
  • Has this ever led to "a DR or a proposal paper"? Perhaps it should be run through std-discussions? – Cubbi Jan 23 '17 at 00:12
  • @Cubbi No significant committee discussion that I recall. You can post this there if you like. – Potatoswatter Jan 23 '17 at 03:08
  • asked on std-duscussion and this was in fact a CWG issue with "insufficient motivation" http://open-std.org/JTC1/SC22/WG21/docs/cwg_closed.html#109 – Cubbi Feb 03 '17 at 11:12
  • @Cubbi Thanks! That's not really prejudice against a proposal, though. It's just too complicated for a DR. (Likely also why implementers excluded it in the first place.) – Potatoswatter Feb 03 '17 at 14:48

1 Answers1

2

The following works fine with C++11:

#include <iostream>

template< typename d >
struct base {
    template< typename >
    struct ct {};

    template< typename >
    void ft() {std::cerr << "cheesecake" << std::endl;}
};

template< typename x >
struct derived : base< derived< x > > {
  template<typename X>
    using ct = typename derived::base::template ct<X>; // new in C++11
  using derived::base::ft;
};

int main()
{
  derived<int>::ct<float> c;
  derived<int> a;
  a.ft<int>();
}

If that is not what you wanted, can you please give an example of how you would want to use ct and ft?

Rumburak
  • 3,416
  • 16
  • 27
  • 1
    Yeah, the template alias works, although it requires re-declaring the template signature which breaks the separation of concerns somewhat. The function `using` declaration does not work inside the scope of `derived`. Perfect forwarding would work. But these aren't using declarations. The added footwork defeats the purpose of a `using` declaration, which is to import a declaration from one scope to a related one *without* declaring something new. Anyway… add the perfect forwarding and I should accept this. – Potatoswatter Mar 18 '13 at 06:10