Могу ли я специализировать аргумент шаблона переменной на основе сигнатуры его оператора () - PullRequest
0 голосов
/ 05 октября 2018

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

template <typename... FunctionList>
void call_all (int i, float f, const FunctionList... function_list);

template <>
void call_all (int, float)
{
}

Я хочу специализировать ее примерно так:

template <typename HasIntArgument, typename... FL>
void call_all (int i, float f, const HasIntArgument & hia, const FL... list)
{
    hia (i);
    call_all (i, f, list...);
}

template <typename HasFloatArgument, typename... FL>
void call_all (int i, float f, const HasFloatArgument & hfa, const FL... list)
{
    hfa (f);
    call_all (i, f, list...);
}

Словом, я хочу, чтобы эта функция для каждой функции-Подобно объекту в function_list, определите, будет ли он вызываться с подписью void(int) или void(float).(Ничто в этом списке не будет вызываться с более чем одной подписью.)

Я хочу, чтобы это работало с необработанными указателями функций, лямбдами или чем-либо с подходящим operator().

Могу ли янаписать подходящую специализацию напрямую, или я должен делать странные вещи с классами черт и SFINAE?

Ответы [ 2 ]

0 голосов
/ 05 октября 2018
template<class...Fs>struct overloaded:Fs...{
  using Fs::operator()...;
};
template<class...Fs>
overloaded(Fs...)->overloaded<Fs...>;

вышеописанное немного сложнее в , но реализации существуют повсеместно.

namespace details {
  struct secret_tag {};
  struct secret_result {
    template<class...Ts>
    secret_tag operator()(Ts&&...) const;
  };
  template<class F>
  using secret_tester = overloaded<std::decay_t<F>, secret_result>;
}      
template<class F, class Arg>
using match_arg_exactly = std::integral_constant<
  bool,
  !std::is_same<
    details::secret_tag, 
    std::result_of_t< details::secret_tester<F>(Arg) >
  >{}
>;

теперь мы можем запросить данный объект, еслион может точно соответствовать конкретному аргументу.

template <typename HasIntArgument>
void call_one(int i, float f, std::true_type, const HasIntArgument & hia)
{
  hia (i);
}
template <typename HasFloatArgument>
void call_one(int i, float f, std::false_type, const HasFloatArgument& hia)
{
  hia (f);
}
template <typename F>
void call_one(int i, float f, const F & hia)
{
  call_one( i, f, match_arg_exactly<const F&, int>{}, hia );
}

, и мы используем это:

void call_all (int, float)
{}

template<class F, class...Fs>
void call_all (int i, float f, F const& f0, Fs const&...fs) {
  call_one( i, f, f0 );
  call_all(i, f, fs...); 
}

Тестовый код:

struct float_eater {
  void operator()(float x)const{ std::cout<< "float "<<x<<"\n"; }
};
struct int_eater {
  void operator()(int x)const{ std::cout<< "int "<<x<<"\n"; }
};

call_all( 42, 3.14, float_eater{}, int_eater{}, int_eater{} );

Живой пример

A overloaded - это что-то вроде:

template<class...Fs>
struct overloaded;

template<class F0>
struct overloaded<F0>:F0 {
  overloaded(F0 f0):F0(std::move(f0)) {}
  using F0::operator();
};

template<class F0, class F1>
struct overloaded<F0, F1>: F0, F1 {
  overloaded( F0 f0, F1 f1 ):F0(std::move(f0)), F1(std::move(f1)) {}
  using F0::operator();
  using F1::operator();
};

template<class F0, class...Fs>
struct overloaded<F0, Fs...>:
  overloaded<F0, overloaded<Fs...>>
{
  overloaded(F0 f0, Fs...fs):
    F0(std::move(f0)),
    overloaded<Fs...>( std::move(fs)... )
  {}
};

, что, на мой взгляд, достаточно для наших целей.(В более общем случае вы либо создаете двоичное дерево, сбалансированное или нет), и обрабатываете совершенную пересылку и ... и т. Д.

0 голосов
/ 05 октября 2018

Вы можете сделать что-то вроде:

#if 0 // C++17
template <typename F>
void dispatch(F func, int i, float f)
{
    if constexpr (has_int_argument<F>::value) {
        func(i);
    } else {
        func(f);
    }
}
#else // C++11
template <typename F>
typename std::enable_if<has_int_argument<F>::value>::type
dispatch(F func, int i, float)
{
    func(i);
}
template <typename F>
typename std::enable_if<!has_int_argument<F>::value>::type
dispatch(F func, int, float f)
{
    func(f);
}
#endif    


template <typename... Fs>
void call_all (int i, float f, const Fs&... fs)
{
    // (dispatch(fs, i, f), ...); // C++17

    const int dummy[] = {0, (dispatch(fs, i, f), 0)...};
    static_cast<void>(dummy);
}

С соответствующими функциональными чертами has_int_argument.что-то вроде:

template <typename ClassOrSig> struct funct_trait;

template <typename C>
struct funct_trait : funct_trait<decltype(&C::operator())> {};

template <typename C, typename Ret, typename ...Args>
struct funct_trait<Ret (C::*) (Args...)> : funct_trait<Ret(Args...)> {};

template <typename C, typename Ret, typename ...Args>
struct funct_trait<Ret (C::*) (Args...) const> : funct_trait<Ret(Args...)> {};

// &&, &, volatile, ... (C ellipsis)

template <typename Ret, typename ...Args>
struct funct_trait<Ret (*)(Args...)> : funct_trait<Ret(Args...)> {};

template <typename Ret, typename ...Args>
struct funct_trait<Ret (Args...)>
{
    using sig_type = Ret(Args...);
    using args_tuple = std::tuple<Args...>;
    // ...
};

template <typename T>
using has_int_argument = std::is_same<std::tuple<int>,
                                      typename funct_trait<T>::args_tuple>;

Демо

...