Передача перегруженной функции-члена в шаблон функции - PullRequest
0 голосов
/ 01 ноября 2018

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

#include <type_traits>
#include <utility>    

struct A {
    constexpr int show(int a, int b) const noexcept {return a + b;}
};

template <typename T, typename MemFn, typename ... Args>
int show(T && obj, MemFn Fn, Args&&... args)
{
    return (obj.*Fn)(std::forward<Args>(args)...);
}

int main()
{
    constexpr A a;
    return show(a, &A::show, 1, 2);
}

, и он работает просто отлично, если в моей структуре есть только одно определение метода show. Как только я добавлю что-то вроде

struct A {
    constexpr int show(int a, int b) const noexcept {return a + b;}
    constexpr int show(int a) const noexcept {return a * 3;}
};

Компилятор не может определить тип функции-члена, и это действительно имеет смысл, но мне было интересно, есть ли обходной путь для этой проблемы, например, встраивание типов входных аргументов в шаблон функции-члена или что-то в этом роде?

Пример кода можно найти здесь .

Ответы [ 3 ]

0 голосов
/ 01 ноября 2018

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

template <typename T, typename... Args>
int show(T && obj, int(std::remove_reference_t<T>::*Fn)(int, int) const, Args&&... args)
{
    return (obj.*Fn)(std::forward<Args>(args)...);
}

Однако это определение может быть слишком ограниченным в зависимости от ваших вариантов использования, поскольку теперь параметр Fn должен точно соответствовать сигнатуре int(int, int) const, включая возможные cv и ref-определители.

0 голосов
/ 01 ноября 2018

Это досадно трудная проблема, которая постоянно приводит к предложениям по языку в попытке ее решить ( P0119 , P0834 , P1170 ).

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

Самый простой способ сделать это - написать лямбду:

[](A& a, auto&&... args) -> decltype(a.show(FWD(args)...)) { return a.show(FWD(args)...); }

Но на самом деле это не так просто и не особенно удобно - и это действительно только в случае, когда show вызывается для не const A. Что если бы у нас были перегрузки const и не const? Или & и &&?

Самый полный способ реализовать это, на мой взгляд, это использовать Boost.HOF с этим макросом :

#define CLASS_MEMBER(T, mem) boost::hof::fix(boost::hof::first_of(\
    boost::hof::match(                                            \
        [](auto, T& s, auto&&... args)                            \
            BOOST_HOF_RETURNS(s.mem(FWD(args)...)),               \
        [](auto, T&& s, auto&&... args)                           \
            BOOST_HOF_RETURNS(std::move(s).mem(FWD(args)...)),    \
        [](auto, T const&& s, auto&&... args)                     \
            BOOST_HOF_RETURNS(std::move(s).mem(FWD(args)...)),    \
        [](auto, T const& s, auto&&... args)                      \
            BOOST_HOF_RETURNS(s.mem(FWD(args)...))),              \
    [](auto self, auto&& this_, auto&&... args)                   \
        BOOST_HOF_RETURNS(self(*FWD(this_), FWD(args)...))        \
    ))

который в вашем случае вы хотите: CLASS_MEMBER(A, show). Это даст вам функциональный объект, который вы можете правильно вызывать:

auto show_fn = CLASS_MEMBER(A, show);
show_fn(a, 1);       // ok, calls a.show(1)
show_fn(a, 1, 2);    // ok, calls a.show(1, 2)
show_fn(a, 1, 2, 3); // error, no matching call - but sfinae friendly
0 голосов
/ 01 ноября 2018

Мне было интересно, есть ли решение этой проблемы, например, встраивание типов входных аргументов в шаблон функции-члена или что-то в этом роде?

Используйте лямбда-выражения вместо объекта и указатель на функцию-член. E.g.:

struct A {
    constexpr int show(int a, int b) const noexcept {return a + b;}
    constexpr int show(int a) const noexcept {return a * 3;}
};

template <typename F, typename ... Args>
int show(F&& f, Args&&... args) {
    return std::forward<F>(f)(std::forward<Args>(args)...);
}

int main() {
    constexpr A a;
    auto f = [&a](auto... args) { return a.show(std::forward<decltype(args)>(args)...); };
    show(f, 1);
    show(f, 1, 2);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...