C ++ - наследовать от базового класса с функцией-членом в качестве аргумента шаблона - PullRequest
1 голос
/ 05 мая 2019

У меня есть очень простой класс, использующий CRTP:

template <typename DerivedType, void (DerivedType::*updateMethod)() = &DerivedType::update>
class UpdatableBase {};

class MyClass : private UpdatableBase<MyClass> {
public:
    void update() {
    }
};

int main() {
    return 0;
}

, который при компиляции с g++ test.cpp -std=c++14 дает следующее:

test.cpp:2:85: error: no member named 'update' in 'MyClass'
template <typename DerivedType, void (DerivedType::*updateMethod)() = &DerivedType::update>
                                                                       ~~~~~~~~~~~~~^
test.cpp:5:25: note: in instantiation of default argument for 'UpdatableBase<MyClass>' required here
class MyClass : private UpdatableBase<MyClass> {
                        ^~~~~~~~~~~~~~~~~~~~~~
1 error generated.

Почему он говорит, что "нет члена"с именем «обновление» в «MyClass» »?Там очень четко есть.

1 Ответ

0 голосов
/ 05 мая 2019

Это очень распространенная проблема с CRTP: компилятор создает определение базового класса, прежде чем читает определение производного класса.

Обходной путь состоит в том, чтобы использовать только определение производного класса внутриопределение члена базового класса :

#include <type_traits>

template <typename DerivedType, class DelayedParameter = void>
class UpdatableBase {
   public:
   //definition of members of templates are instantiated when needed!
   void update(){
      if constexpr(std::is_same_v<DelayedParameter,void>)
        static_cast<DerivedType>(this)->update();
      else
        (static_cast<DerivedType*>(this)->*DelayedParameter::value)();
     }
  };

class MyClassDelayedParameter;

class MyClass:public UpdatableBase<MyClass,MyClassDelayedParameter> 
  //the compiler only instantiate the definition of the base class:
  //it will only instantiate the DECLARATION of the base class members.
  {
  public:
    void update();
  };

//Here MyClass is defined so we can access its member.
struct MyClassDelayedParameter:
  std::integral_constant<void(MyClass::*)(),&MyClass::update>
  {};

//UpdatableBase<...>::update DEFINITION is instantiated here. At this point
//of instantiation, MyClass and MyClassDelayedParameter are defined.
int main() {
    MyClass a;
    UpdatableBase<MyClass,MyClassDelayedParameter>& b=a;
    b.update();
    return 0;
}

DEMO

...