Явная специализация функции-члена с переменными аргументами шаблона - PullRequest
3 голосов
/ 30 мая 2019

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

У меня есть функция-член "emit", которая будет создавать объект Event из предоставленных параметров шаблона variadic и пересылать событие.Однако я хотел бы предоставить специализированную функцию «emit», которая будет определять, является ли предоставленный аргумент заранее созданным объектом Event, и в этом случае я мог бы переслать событие, не делая лишней копии.

Мой начальныйпопытка ...

template <typename T_Event>
class EventHandler
{
public:

  template <typename... T_Args>
  void emit(T_Args&&... args)
  {
    printf("variadic\n");
    deliver(T_Event {std::forward<T_Args>(args)...});
  }

  void emit(const T_Event& event)
  {
    printf("reference\n");
    deliver(event);
  }

  ...
};

Я использовал следующую логику, чтобы попробовать обе функции испускает, но оказывается, что VARIADIC шаблон функция всегда имеет приоритет над константной опорной функцией.

1009 *

После еще одного исследования мне удалось достичь своей цели, определив две версии функции шаблона variadic и используя enable_if для выполнения правильной.

  template <typename... T_Args>
  void emit(T_Args&&... args)
  {
    printf("variadic\n");
    T_Event event {std::forward<T_Args>(args)...};
    deliver(event);
  }

  template <typename... T_Args, typename = std::enable_if<std::is_same<const T_Event&, T_Args...>::value>>
  void emit(T_Args&&... args)
  {
    printf("reference\n");
    deliver(std::forward<T_Args>(args)...);
  }

Это решение работало именно так, как мне нужно, когда я компилируюс GCC, но если я скомпилирую с CLANG, я получаю следующее сообщение об ошибке:

  call to member function 'emit' is ambiguous
handler.emit(event);

  candidate function [with T_Args = <EventB &>]
void emit(T_Args&&... args)

  candidate function [with T_Args = <EventB &>, $1 =
  std::__1::enable_if<false, void>]
void emit(T_Args&&... args)

Я предполагаю, что я близок к правильному решению, может кто-нибудь объяснить, что я делаю неправильно?

1 Ответ

1 голос
/ 30 мая 2019

Хитрость заключается в том, чтобы заставить шаблон переменной отклонить разрешение перегрузки для пакета параметров, который состоит из одного (необязательного ссылки на необязательный const-квалифицированного) параметра.

Это делается путем удаления ссылок и констант-qualifiers от каждого типа в пакете параметров, собирая кортеж из них и используя std::is_same, как и в вашей попытке, сравнить результирующий тип с std::tuple<T_Event>, а затем в этом случае не удается разрешить перегрузку, отрицаячерты типа (это не std::tuple<T_Event>.

#include <type_traits>
#include <tuple>
#include <iostream>

template <typename T_Event>
class EventHandler
{
public:

    template <typename... T_Args,
          typename=std::enable_if_t
          <std::negation<std::is_same
                 <std::tuple<std::remove_const_t
                         <std::remove_reference_t
                          <T_Args>>...>,
                          std::tuple<T_Event>>
                              >::value>>
    void emit(T_Args&&... args)
    {
        std::cout << "Variadic" << std::endl;
    }

    void emit(const T_Event& event)
    {
        std::cout << "Reference" << std::endl;
    }
};

int main()
{
    EventHandler<const char *> foo;
    const char *bar="foobar";

    foo.emit(4);
    foo.emit(4, "bar");
    foo.emit(bar);
    return 0;
}

Протестировано с g ++ с -std = c ++ 17. Это должно быть выполнимо с C ++ 11 и C ++ 14 путем повторной реализации некоторых изчерты отсутствующего типа. Конечный результат:

$ g++ -o t -std=c++17 t.C
$ ./t
Variadic
Variadic
Reference
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...