Можно ли извлечь типы аргументов из сигнатуры функции (члена Functor) для использования в шаблоне? - PullRequest
4 голосов
/ 12 июля 2011

Предположим, у вас есть функтор:

struct MyFunctor
{
    bool operator ()( int value )
    {
        return true;
    }
};

Возможно ли получить тип аргумента члена функтора для использования в вашем шаблоне?Ниже приводится использование этой мифической функциональности:

template < typename FunctorType >
bool doIt( FunctorType functor, typename FunctorType::operator()::arg1 arg )
{
    return functor( arg );
}

Существует ли правильный синтаксис, который заменит мой мифический FunctorType::operator()::arg1?

Ответы [ 4 ]

6 голосов
/ 01 мая 2012

Если вы знаете, что предмет является функтором, то вы можете просто взять его operator(), вот так:

#include <iostream>

template <unsigned Idx, typename... T>
struct pick
{
    static_assert(Idx < sizeof...(T), "cannot index past end of list");
};

template <typename T, typename... TRest>
struct pick<0U, T, TRest...>
{
    typedef T result;
};

template <unsigned Idx, typename T, typename... TRest>
struct pick<Idx, T, TRest...>
{
    typedef typename pick<Idx-1, TRest...>::result result;
};

template <typename Func>
struct func_traits;

template <typename TObj, typename R, typename... TArgs>
struct func_traits<R (TObj::*)(TArgs...)>
{
    typedef R result_type;

    template <unsigned Idx>
    struct argument
    {
        typedef typename pick<Idx, TArgs...>::result type;
    };
};

template <typename Func,
          typename Traits = func_traits<Func>,
          typename R = typename Traits::result_type,
          typename Arg0 = typename Traits::template argument<0>::type,
          typename Arg1 = typename Traits::template argument<1>::type
         >
void foo(Func f)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
};

struct thing
{
    void operator()(long, int*) { }
};

int main()
{
    foo(&thing::operator());
}

Для меня эта программа печатает:

void foo(Func) [with Func = void (thing::*)(long int, int*), Traits = func_traits<void (thing::*)(long int, int*)>, R = void, Arg0 = long int, Arg1 = int*]

Ключевым моментом является то, что Arg0 и Arg1 равны long и int* соответственно.

4 голосов
/ 12 июля 2011

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

В качестве альтернативы, вы можете просто сделать тип аргумента вторым параметром шаблона:

template < typename FunctorType, class ArgumentType >
bool doIt( FunctorType functor, ArgumentType arg )
{
    return functor( arg );
}

Компилятор по-прежнему будет жаловаться, если ArgumentType не соответствует типу, требуемому функтором.

3 голосов
/ 12 июля 2011

Вы можете сделать это в C ++ 0x

template <typename... Args>
struct Function {
    typedef std :: tuple <Args...> args;
    void call () (Args... args);
}

template <typename... Args>
void do_it (Function<Args...>::args:: SOMETHING :: type t, Args... args) {
    something (t)
    Function <Args...> :: call (args...);
}
2 голосов
/ 25 января 2014

Здесь я даю C ++ 11 обновление для @ BjörnPollex (правильный) ответ.

Возвращаясь к вопросу, вы хотите явно указать второй аргумент doIt, главным образом, для ограничения того, что можно передать. В C ++ 11 вы можете подразумевать это ограничение, не зная явно тип аргумента функтора (который не очень хорошо определен, если функтор все равно перегружен).

template < typename FunctorType, class ArgumentType >
auto doIt( FunctorType functor, ArgumentType arg ) -> decltype(bool(functor(arg)))
{
    return functor( arg );
}

(преобразование в bool может даже не понадобиться, я поместил его здесь, потому что кажется, что вы действительно хотите, чтобы возвращаемый тип был bool).

Эта doIt (шаблонная) функция будет принимать любой аргумент, который, возможно, совместим с аргументом functor (и также может быть преобразован в bool). Если переданный аргумент несовместим, функция вообще не будет существовать и выдаст изящную ошибку компилятора «doIt function not found».

Можно сделать еще один шаг, используя совершенную перемотку вперед, чтобы сделать doIt в точности эквивалентным functor(arg):

template < typename F, class A >
auto doIt( F&& f, A&& a ) -> decltype(bool(std::forward<F>(f)(std::forward<A>(a))))
{
    return std::forward<F>(f)( std::forward<A>(a) );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...