Использование SFINAE для обеспечения того, чтобы аргументы пакета параметров Variadi c были производными от определенного типа c - PullRequest
0 голосов
/ 09 июля 2020

В настоящее время у меня есть иерархия классов со следующей структурой:

#include <list>
#include <memory>

class Base {
protected:
    std::list<std::shared_ptr<Base>> items_;
    Base(){}
public:
    addItem(Base* derived) { 
        // adds derived to items_
        // and checks for duplicates
    }
};

class DerivedA : public Base {
public:
    DerivedA() {}
}

class DerivedB : public Base {
public:
    DerivedB() {}
}

И я могу использовать их следующим образом:

int main() { 
    DerivedA a1, a2, a3;
    DerivedB b1, b2, b3;

    a1.addItem(&a2); a1.addItem(&a3); a1.addItem(&b1); a1.addItem(&b2); a1.addItem(&b3);
    a2.addItem(&a1); a2.addItem(&a3); a2.addItem(&b1); a2.addItem(&b2); a2.addItem(&b3);
    a3.addItem(&a1); a3.addItem(&a2); a3.addItem(&b1); a3.addItem(&b2); a3.addItem(&b3);       

    b1.addItem(&a1); b1.addItem(&a2); b1.addItem(&a3); b1.addItem(&b2); b1.addItem(&b3);
    b2.addItem(&a1); b2.addItem(&a2); b2.addItem(&a3); b2.addItem(&b1); b2.addItem(&b3);
    b3.addItem(&a1); b3.addItem(&a2); b3.addItem(&a3); b3.addItem(&b1); b3.addItem(&b2);

    return 0;
}

Как видите, здесь много избыточности в звонках ::addItem(). Я бы хотел использовать их так:

int main() {
    DerivedA a1, a2, a3;
    DerivedB b1, b2, b3;

    a1.addItem(&a2, &a3, &b1, &b2, &b3);
    a2.addItem(&a1, &a2, &b1, &b2, &b3);
    a3.addItem(&a1, &a2, &b1, &b2, &b3);

    b1.addItem(&a1, &a2, &a3, &b2, &b3);
    b2.addItem(&a1, &a2, &a3, &b1, &b3);
    b3.addItem(&a1, &a2, &a3, &b1, &b2);

    return 0;
}

Итак, я подумываю использовать шаблон функции Variadi c с SFINAE для функции ::addItem() в моей абстрактной базе class ... Требования следующие:

  • Я передам адрес объекта (функция принимает указатель на объект), поскольку базовый класс хранит список shared_ptr<Base>
  • Мне нужно убедиться, что каждый параметр с пакетом параметров шаблона функции Variadi c является производным типом Base.

Это то, что я до сих пор пытались:

template<typename... Args, std::enable_if_t<std::is_base_of_v<Base, Args>...> { // How to use SFINAE here?
void addItem(Args*&& ... args) {
    for ( auto& it : items_ ) { 
        // I also need to check a member variable from each of the `Args...` or derived types
        // ::foo() is not shown in the above classes but exists in my code (its not important here)
        // what is important is how do I expand (args) within an if statement?
        if ( args->foo() == l->foo() ) { 
            // do something
        }
        // add to list
    }
}

Я пытаюсь преобразовать свою обычную функцию-член, чтобы она работала, как описано выше, поскольку я думаю об использовании как шаблонов функций Varidia c, так и SFINAE, чтобы убедиться, что каждый аргумент пакета параметров, по крайней мере, получен из Base.

Я не уверен, какой правильный синтаксис или набор библиотечных функций следует использовать ... Я пробовал использовать is_same , is_base_of, conjunction, а nd другие шаблоны функций std ... И еще одна проблема - как правильно расширить пакет параметров в операторе if. Есть ли специальная нотация c с ... или мне нужно использовать выражения свёртки?

1 Ответ

1 голос
/ 09 июля 2020

Было бы намного проще просто добавить перегрузку, которая принимает initializer_list:

addItem(std::initializer_list<Base*> items)
{
    for(auto item: items)
        addItem(item);
}

И вы бы просто вызывали это с помощью списка инициализации в фигурных скобках: a1.addItem({&a2, &a3, &b1, &b2, &b3}); .

...