Адрес перегруженной шаблонной функции C ++, включающей SFINAE - PullRequest
1 голос
/ 04 июня 2019

Мне нужно получить адрес перегруженной шаблонной функции, которая включает SFINAE.Хорошим примером этого сценария будет Boost :: asio :: spawn, найденный здесь ...

https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/spawn.html

Как мне найти адрес этого конкретного экземпляра ...

template<
    typename Function,
    typename Executor>
void spawn(
    const Executor & ex,
    Function && function,
    const boost::coroutines::attributes & attributes = boost::coroutines::attributes(),
    typename enable_if< is_executor< Executor >::value >::type* = 0);

Я безуспешно пытался это ...

using Exec = boost::asio::io_context;
using Func = std::function<void(boost::asio::yield_context)>;
void (*addr)(Exec, Func) = boost::asio::spawn;

Ответы [ 2 ]

4 голосов
/ 04 июня 2019

boost::asio::spawn не является функцией. Это функция шаблон . Это план, из которого можно создавать функции. Нет способа получить указатель на шаблон функции, потому что это чисто конструкция времени компиляции.

boost::asio::spawn<Func, Exec> - это набор перегрузок функций, но он не имеет перегрузок, соответствующих сигнатуре void(Exec,Func). Помните, что аргументы функций по умолчанию являются просто синтаксическим сахаром. Эти аргументы все еще являются частью сигнатуры функции.

Эти две проблемы делают указатель на boost::asio::spawn трудным и уродливым. Было бы намного проще использовать лямбду. Лямбда позволит вам сохранить вывод типа и использовать аргументы по умолчанию:

auto func = [](auto&& exec, auto&& func) {
    boost::asio::spawn(std::froward<decltype(exec)>(exec),
                       std::forward<decltype(func)>(func));
};

Даже если вам абсолютно необходим указатель на функцию, лямбда, вероятно, по-прежнему способ. Вы теряете вывод типа параметра, но все же можете использовать аргументы функции по умолчанию:

void(*addr)(const Exec&, Func) = [](const Exec& exec, Func func) {
    boost::asio::spawn(exec, std::move(func));
};

Это работает, потому что лямбды без захвата могут быть преобразованы в необработанные указатели функций.

Если вам действительно по какой-то причине необходим указатель на один из spawn экземпляров, вы можете его получить, но это не очень красиво:

using Exec = boost::asio::io_context::executor_type;
using Func = std::function<void(boost::asio::yield_context)>;

void(*addr)(const Exec&, Func&, const boost::coroutines::attributes&, void*) = boost::asio::spawn<Func&, Exec>;

Хотя вы многое теряете. Вы не только теряете дедукцию типа аргумента и аргументы по умолчанию, вы также теряете возможность передавать как lvalues, так и rvalues ​​функции, поскольку у вас больше нет выведенного контекста для пересылки ссылок для работы. Я должен получить указатель на экземпляр принимает lvalue-ссылку на функцию. Если вы хотите, чтобы он вместо этого принимал rvalue-ссылки, используйте

void(*addr)(const Exec&, Func&&, const boost::coroutines::attributes&, void*) = boost::asio::spawn<Func, Exec>;

Также обратите внимание, что эта функция принимает четыре параметра. Назовите это с, то есть

addr(my_io_context.get_executor(), my_function, boost::coroutines::attributes{}, nullptr);

Пример * +1031 *

0 голосов
/ 04 июня 2019

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

using Exec = boost::asio::io_context;
using Func = std::function<void(boost::asio::yield_context)>;
using Attr = boost::coroutines::attributes;

void (*addr)(const Exec&,
             Func&&,
             const Attr&,
             typename enable_if< is_executor< Executor >::value>::type*
            ) = boost::asio::spawn<Func, Exec>;

Да, некрасиво, но не так уж и плохо.

...