Можно ли использовать CRTP с производными классами, которые сами являются шаблонами? - PullRequest
0 голосов
/ 04 ноября 2019

Я пытаюсь использовать CRTP с производными классами, которые сами по себе шаблонизируются и сталкиваются с ошибками компилятора, которые я не знаю, как исправить.

Есть ли способ сделать то, что я хочу?

Код здесь.

#include <iostream>

// Parent class to use in CRTP
template <typename T>
class Singleton {
    public:

    static T& getInstance() {
        Protector p;
        static T instance(p);
        return instance;
    }

    // Singleton pattern - no copying!
    Singleton(const Singleton&) = delete;
    Singleton(Singleton&&) = delete;
    const Singleton& operator=(const Singleton&) = delete;
    Singleton& operator=(Singleton&&) = delete;

protected: // so only derived classes can construct Singletons
    Singleton() = default;
    struct Protector {};    // avoids having to make Singleton a friend of descendents.
    virtual ~Singleton() = default;
};


// Simple example of using the CRTP, this one compiles and runs as expected
class Counter final : public Singleton<Counter> {
    private:
        int _counter;
    public:
        Counter (Protector) : _counter(0) {std::cout << "Counter Constructor" << std::endl;}
        ~Counter () {std::cout << "Counter Destructor" << std::endl;}
        int inc() {return ++_counter;}
        int dec() {return --_counter;}
        const int operator()() const {return _counter;}
};

// More complex example of using the CRTP. It generates compiler errors
template <typename T>
class TCounter final : public Singleton<TCounter<T>> {
    private:
        T _counter;
    public:
        TCounter (Protector) : _counter(0) {std::cout << "TCounter Constructor" << std::endl;}
        ~TCounter() {std::cout << "TCounter Destructor" << std::endl;}
        T inc() {return ++_counter;}
        T dec() {return --_counter;}
        const T operator()() const {return _counter;}
};

int main () {
    using namespace std;
    Counter& ctr = Counter::getInstance();
    cout << ctr() << ", " << ctr.inc() << ", " << ctr.dec() << endl;

    using FCounter = TCounter<float>;
    FCounter& fctr = FCounter::getInstance();
    cout << fctr() << ", " << fctr.inc() << ", " << fctr.dec() << endl;
    return 0;
}

вывод компилятора здесь

$ g++ src/goof.cpp -o goof
src/goof.cpp:45:22: error: function definition does not declare parameters
   TCounter (Protector) : _counter(0) {std::cout << "TCounter Constructor" << std::endl;}
                      ^
src/goof.cpp: In instantiation of ‘static T& Singleton<T>::getInstance() [with T = TCounter<float>]’:
src/goof.cpp:58:29:   required from here
src/goof.cpp:10:12: error: no matching function for call to ‘TCounter<float>::TCounter(Singleton<TCounter<float> >::Protector&)’
   static T instance(p);
            ^~~~~~~~
src/goof.cpp:41:7: note: candidate: ‘TCounter<float>::TCounter()’
 class TCounter final : public Singleton<TCounter<T>> {
       ^~~~~~~~
src/goof.cpp:41:7: note:   candidate expects 0 arguments, 1 provided

Вещи, которые я не понимаю:

  • почему компилятор считает, чтокандидат конструктор принимает нулевые аргументы? (строка 41)
  • он жалуется, что я не предоставляю параметр (строка 45), но когда я это делаю, он выдает другую ошибку - ожидая ')' после Protector.

Заранее спасибо за вашу помощь.

1 Ответ

2 голосов
/ 04 ноября 2019

Все, что вам нужно сделать, это осторожно направить компилятор рукой, чтобы сказать ему, как решить Protector.

template <typename T>
class TCounter final : public Singleton<TCounter<T>> {
public:
  using Protector = typename Singleton<TCounter<T>>::Protector;
};
...