Что вызывает сбой компиляции? Это подпись объявления или внутри самой реализации функции? - PullRequest
0 голосов
/ 08 июля 2020

Я работаю над функцией-членом Component класса Component::connect(). Component - абстрактный базовый класс для всех других типов компонентов в моей библиотеке.

Сейчас я модифицирую этот метод, чтобы использовать Variadi c Шаблоны функций и SFINAE, чтобы разрешить неизвестные множественные входные данные в эту функцию ... Все входные данные должны быть производными от типа Component.

У меня проблемы с синтаксисом его объявления, а также с его реализацией ... Я пробовал разные вещи при получении различных ошибок компилятора. Это может иметь какое-то отношение к расширению пакета параметров или к тому, как я пытаюсь использовать SFINAE в его сигнатуре.

Вот текущая сигнатура моего класса и функции, над которыми я работаю ...


class Component {
protected:
    std::string id_ = "";
    std::list<std::shared_ptr<Component>> components_;

    explicit Component(const std::string& id) : id_{ id } {}

public:
    virtual ~Component() {}

    std::string& id() { return id_; }

    // Trying to use both Variadic Function Template and SFINAE for this function!
    // I may eventually remove this from here and move it to another class using CRTP...
    template<typename ... Args, std::enable_if_t <std::is_class<Args...>{}, Component> = 0 >
    void connect(Args&& ...args) {
        for (auto& l : components_) {
            if (args->id_ == l->id()) {
                std::cout << "Component " << (args->id_) << " already exists in " << id_ << "!\n";
                return;
            }
        }
        components_.push_back( std::make_shared<Component>( args... ) );
        std::cout << "Successfully connected " << args->id() << " to " << id_ << "\n";
    }

    virtual std::list<std::shared_ptr<Component>> myConnections() { return components_; }
    virtual void propagate() {};
}; 

Каким будет правильный синтаксис для успешной компиляции этого в Visual Studio 2017 с использованием C ++ 17? Я пытаюсь использовать оба шаблона функций Variadi c с использованием SFINAE.


Edit

До того, как я изменил свою подпись, производные классы, такие как как Wire, производные от Component, я мог использовать их следующим образом:

Wire w1, w2, w3, w4;

w1.connect(&w2);
w1.connect(&w3);
w1.connect(&w4);

w2.connect(&w3);
w2.connect(&w4);

Я хотел бы использовать ту же функцию, но таким образом:

w1.connect(&w2, &w3, &w4);

Вот почему я хочу использовать шаблон функции Variadi c и почему я хочу использовать SFINAE, чтобы убедиться, что любой тип, переданный в ::connect(), в котором все производные классы могут его использовать, фактически является производным с Component!

Ответы [ 2 ]

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

Не уверен, что понимаете, что именно вы хотите (и без полного примера сложнее), но ...

Все входные данные должны быть производными от типа Component.

Итак, если я правильно понимаю (и учитывая, что вы пометили C ++ 17, поэтому вы можете использовать сворачивание), подпись метода должна быть

  template <typename ... Args,
            std::enable_if_t<(... && std::is_base_of<Component, Args>{}),
                             int> = 0>
  void connect (Args && ... args)

или, возможно,

  template <typename ... Args>
  std::enable_if_t<(... && std::is_base_of<Component, Args>{})>
      connect (Args && ... args)

У меня проблемы с синтаксисом его объявления, а также с его реализацией ...

Что касается реализации, я полагаю, что более серьезной проблемой является цикл; может быть, вы можете попробовать

     for ( auto & l : components_ )
      {
        std::string  aid;

        if ( true == ( ... || (args->id_ == l->id()
                               ? (aid = args->id_, true)
                               : false) ) )
         {
           std::cout << "Component " << aid
              << " already exists in " << id_ << "!\n";

           return;
         }
      }

или, может быть,

     for ( auto & l : components_ )
      {
        std::string  aid;

        ( ... , (args->id_ == l->id() ? aid = args->id_ : args->id_) );

        if ( false == aid.empty() )
         {
           std::cout << "Component " << aid
              << " already exists in " << id_ << "!\n";

           return;
         }
      }

или что-то подобное.

Но насчет

    std::cout << "Successfully connected " << args->id() << " to " << id_ << "\n";

откровенно ... Я не знаю.

1 голос
/ 09 июля 2020
error: expression contains unexpanded parameter pack 'args'
           if (args->id_ == l->id()) {

Вы можете разделить connect() на две функции для работы с текущим нерасширенным пакетом параметров. Одна функция, которая выполняет проверку и подключение, и другая, которая использует выражение свертки:

template<typename T> // connect one only
void connect(T&& arg) {
    static_assert(std::is_base_of_v<Component, std::remove_reference_t<T>>,
                  "Must be based on Component");

    for (auto& l : components_) {
        if (arg.id_ == l->id()) {
            std::cout << "Component " << arg.id_ << " already in " << id_ << "\n";
            return;
        }
    }
    components_.push_back( std::make_shared<Component>( std::forward<T>(arg) ) );
    std::cout << "Successfully connected " << arg.id() << " to " << id_ << "\n";
}

template<typename... Args> // folding
void connect(Args&& ...args) {
    (connect(std::forward<Args>(args)), ...);
}

Подключение будет выполняться следующим образом:

w1.connect(w2, w3, w4);

Demo ( с битами из старой ревизии вашего вопроса)

...