Экземпляр класса интерфейса обратного вызова шаблона Variadic не может быть перегружен

То, что я пытаюсь сделать, это иметь интерфейс обратного вызова только с функциональными членами. Сегодня у меня есть 2 последних класса, которые используют этот интерфейс для определения своего поведения, сохраняя определенные обратные вызовы в CallbackStore.

Из моих ограничений (здесь) я получил следующий минимальный код компиляции, но только с одним дочерним классом:

#include <iostream>
#include <stdint.h>

using namespace std;

static constexpr int STORE_SIZE = 4;

void* operator new(size_t size)
{
    cout << "ERROR HEAP USED" << endl;
}

template<typename T, size_t storeSize>
class CallbackStore
{
public:

    CallbackStore() : that_(nullptr) {};
    CallbackStore(T* that) : that_(that) {};

    using CallbackCondition = bool (*) (T*);
    using CallbackAction = void (*) (T*,int);
    struct Step
    {
        CallbackCondition pCallbackCondition;
        CallbackAction pCallbackAction;
    };
    void setStep(int stepId,CallbackCondition pCallbackCondition, CallbackAction pCallbackAction)
    {
        if (stepId<storeSize)
        {
            store[stepId].pCallbackCondition = pCallbackCondition; 
            store[stepId].pCallbackAction = pCallbackAction; 
        }
        else
        {
            cout << "pointer error" << endl;
        }
    }
    void callStep(int stepId, int param) 
    {
        if ((stepId<storeSize) &&
        (store[stepId].pCallbackCondition != nullptr) &&
        (store[stepId].pCallbackAction != nullptr) &&
        (that_ != nullptr))
        {
            bool isActive =  (*(store[stepId].pCallbackCondition))(that_);
            if (isActive) {(*(store[stepId].pCallbackAction))(that_,param);}
        }
        else
        {
            cout << "pointer error" << endl;
        }

    }
    Step store[storeSize];
    T* that_;
};

template<typename Base, typename... ArgT>
class Interface : public Base // interface
{
public:
    Interface() : Base() {};
    Interface(ArgT... arg) : Base(arg...) {};

public:
    static bool True(Base* baseInstance)
    {
        return true;
    }
    static bool IsNegative(Base* baseInstance)
    {
        return ((static_cast<Base*>(baseInstance))->getValue() < 0);
    }
    static bool IsNull(Base* baseInstance)
    {
        return ((static_cast<Base*>(baseInstance))->getValue() == 0);
    }
    static bool IsPositive(Base* baseInstance)
    {
        return (IsNegative(baseInstance) == false);
    }
    static void PrintValue(Base* baseInstance, int value)
    {
        cout << "print this value : " << value << "." << endl;
    }
};


template<typename Base>
class Interface<Base,void>// interface
{
public:
    Interface() : Interface<Base,void>() {};
};

class MotherA
{
public:
    MotherA(){};
    MotherA(int x): x_(x){};
    int getValue() { return x_; }
    void setValue(int x) { x_ = x; }

protected:
    int x_ = -3; 
};

class ListModel : public MotherA
{
};
class FinalLChild : public Interface<ListModel>, public CallbackStore<ListModel, STORE_SIZE>
{
public:
    FinalLChild(): Interface(), CallbackStore(this)
    {
        setStep(0, &Interface::IsNegative,  &Interface::PrintValue ); 
        setStep(1, &Interface::IsNegative,  &Interface::PrintValue ); 
        setStep(2, &Interface::IsNull,      &Interface::PrintValue ); 
        setStep(3, &Interface::True,        &Interface::PrintValue ); 
    };

};

class ValueModel : public MotherA
{
public:
    ValueModel() : MotherA(), y_(0) {};
    ValueModel(int x,int y) : MotherA(x), y_(y) {};
    void reset(){x_= y_;};
    int y_ = 0;
};

class ValueChild : public Interface<ValueModel,int,int>, public CallbackStore<ValueModel, STORE_SIZE>
{
public:
    ValueChild() : Interface(), CallbackStore(nullptr){};
    ValueChild(int x,int y): Interface(x,y), CallbackStore(this){};
};

class FinalVChild : public ValueChild
{
public:
    FinalVChild():ValueChild(2,0)
    {
        setStep(0, &Interface::IsPositive,  &Interface::PrintValue ); 
        setStep(1, &Interface::IsPositive,  &Interface::PrintValue ); 
        setStep(2, &Interface::IsNull,      &Interface::PrintValue ); 
        setStep(3, &Interface::IsNull,      &Interface::PrintValue ); 
    };
};


int main()
{
    FinalVChild c;
    for(int i = 0; i < STORE_SIZE; i++)
    {
        c.callStep(i,8);
    }
    cout << "reset:\n";
    c.reset();
    for(int i = 0; i < STORE_SIZE; i++)
    {
        c.callStep(i,8);
    }
    // shall print "print this value : 8." 3 times if x_ is null, twice if x_ is negative.
}

Добавляя новый дочерний класс,

class ListModel : public MotherA
{
};

class FinalLChild : public Interface<ListModel>, public CallbackStore<ListModel, STORE_SIZE>
{
public:
    FinalLChild(): Interface(), CallbackStore(this)
    {
        setStep(0, &Interface::IsNegative,  &Interface::PrintValue ); 
        setStep(1, &Interface::IsNegative,  &Interface::PrintValue ); 
        setStep(2, &Interface::IsNull,      &Interface::PrintValue ); 
        setStep(3, &Interface::True,        &Interface::PrintValue ); 
    };

};

и в основном()

    FinalLChild d;
    cout << "d:\n";
        for(int i = 0; i < STORE_SIZE; i++)
    {
        d.callStep(i,8);
    }

это вызывает следующую ошибку компиляции:

main.cpp: In instantiation of ‘class Interface<ListModel>’:
<span class = "error_line" onclick = "ide.gotoLine('main.cpp',113)">main.cpp:113:57</span>:   required from here
main.cpp:65:5: error: ‘Interface::Interface(ArgT ...) [with Base = ListModel; ArgT = {}]’ cannot be overloaded
     Interface(ArgT... arg) : Base(arg...) {};
     ^~~~~~~~~
main.cpp:64:5: error: with ‘Interface::Interface() [with Base = ListModel; ArgT = {}]’
     Interface() : Base() {};
     ^~~~~~~~~
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
38
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вместо специализации шаблона

template<typename Base>
class Interface<Base,void>// interface
{
public:
    Interface() : Interface<Base,void>() {};
};

class ListModel : public MotherA
{
};
class FinalLChild : public Interface<ListModel>, public CallbackStore<ListModel, STORE_SIZE>
{
public:
    FinalLChild(): Interface(), CallbackStore(this)
    {
        setStep(0, &Interface::IsNegative,  &Interface::PrintValue ); 
        setStep(1, &Interface::IsNegative,  &Interface::PrintValue ); 
        setStep(2, &Interface::IsNull,      &Interface::PrintValue ); 
        setStep(3, &Interface::True,        &Interface::PrintValue ); 
    };

};

Используйте class NoneT {};

class NoneT {};

class ListModel : public MotherA
{
};
class FinalLChild : public Interface<ListModel,NoneT>, public CallbackStore<ListModel, STORE_SIZE>
{
public:
    FinalLChild(): Interface(), CallbackStore(this)
    {
        setStep(0, &Interface::IsNegative,  &Interface::PrintValue ); 
        setStep(1, &Interface::IsNegative,  &Interface::PrintValue ); 
        setStep(2, &Interface::IsNull,      &Interface::PrintValue ); 
        setStep(3, &Interface::True,        &Interface::PrintValue ); 
    };

};
Ответ принят как подходящий

Я полагаю, проблема в этой паре конструкторов

Interface() : Base() {};
Interface(ArgT... arg) : Base(arg...) {};

Когда ArgT... — пустой список, они являются одним и тем же конструктором и сталкиваются.

Я предлагаю SFINAE включить/отключить второй следующим образом

template <bool B = (sizeof...(ArgT)>0u), std::enable_if_t<B, bool> = true>
Interface(ArgT... arg) : Base(arg...) {};

Таким образом, вторая конструкция активна только тогда, когда ArgsT... не пуст.

Альтернативой может быть отказ от SFINAE (второй когда-либо включенный конструктор), но преобразование его в шаблон с вариативным безымянным списком неиспользуемых параметров шаблона, поэтому первый имеет приоритет

Interface() : Base() {};

template <typename ...>
Interface(ArgT... arg) : Base(arg...) {};

Другие вопросы по теме