3

Here's a minimal version of what I'm trying to do:

template<typename D>
struct Base {

        void common() {
                // ... do something ...
                static_cast<D *>(this)->impl();
                // ... do something ...
        }

        void common_with_arg(typename D::Arg arg) {
                // ... do something ...
                static_cast<D *>(this)->impl_with_arg(arg);
                // ... do something more ...
        }
};

struct Derived : Base<Derived> {
        void impl() { }

        using Arg = int;
        void impl_with_arg(Arg arg) { }

};

Base::common() and Derived::impl() work OK (as expected). Base::common_with_arg() and Derived::impl_with_arg(), however, do not.

With gcc, for example, I get the following error:

1.cc: In instantiation of ‘struct Base<Derived>’:
1.cc:18:18:   required from here
1.cc:11:7: error: invalid use of incomplete type ‘struct Derived’
  void common_with_arg(typename D::Arg arg) {
       ^~~~~~~~~~~~~~~
1.cc:18:8: note: forward declaration of ‘struct Derived’
 struct Derived : Base<Derived> {

Intuitively (without understanding all details about template instantiation), this seems like a sensible error. Is there another way to achieve the same functionality?

ynimous
  • 4,642
  • 6
  • 27
  • 43

1 Answers1

5
void common_with_arg(typename D::Arg arg)
//                            ^^^^^^

You cannot access D::Arg here, as the definition of Derived would be required. But the definition is never available as the Base template is being instantiated here...

struct Derived : Base<Derived> { 
//               ^^^^^^^^^^^^^

...where Derived is not yet fully-defined.


One possible workaround is making common_with_arg a function template:

template <typename T>
void common_with_arg(T&& arg) {
        // ... do something ...
        static_cast<D *>(this)->impl_with_arg(std::forward<T>(arg));
        // ... do something more ...
}

example on wandbox


If you really need the Arg type alias, read this question:
"C++ static polymorphism (CRTP) and using typedefs from derived classes".

Community
  • 1
  • 1
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • Thanks! I came up with a similar solution a few minutes after writing the question, but yours is better since I used `(T arg)` instead of `(T&& arg)`. – ynimous Jan 09 '17 at 15:22
  • I'd rather suggest `template void common_with_arg(typename T::Arg&& arg) { static_cast(this)->impl_with_arg(std::forward(arg)); } `. So that `arg` is constrained to type `D::Arg` as shown in the example code. cc @ynimous – skypjack Jan 10 '17 at 22:41