3

我有以下模板功能:

template <typename...Args, typename Func>
void call(const char *name, Args...args, Func f)
{
        f(3);
}

当我尝试使用它时,比如

    call("test", 1, 2, 3, [=](int i) { std::cout<< i; });

编译器抱怨它无法推断模板参数Func。如何解决这个问题,知道args可以是除函数指针之外的任何类型。

4

3 回答 3

8

从 14.1p11 开始:

函数模板的模板形参包后面不应跟随另一个模板形参,除非该模板形参可以从函数模板的形参类型列表中推导出来或具有默认实参(14.8.2)。

如果要将可调用对象保留为最后一个参数,可以使用forward_as_tuple

template <typename...Args, typename Func>
void call(const char *name, std::tuple<Args...> args, Func f)
{
        f(3);
}

call("test", std::forward_as_tuple(1, 2, 3), [=](int i) { std::cout<< i; });

tuple实际上,我们还可以通过合成包含可调用对象来做得更好:

#include <tuple>

template<typename... Args_F>
void call_impl(const char *name, std::tuple<Args_F... &&> args_f) {
   auto &&f = std::get<sizeof...(Args_F) - 1>(args_f);
   f(3);
}

template<typename...ArgsF>
void call(const char *name, ArgsF &&...args_f) {
   call_impl(name, std::tuple<ArgsF &&...>(std::forward<ArgsF>(args_f)...));
}
于 2013-04-12T17:54:54.483 回答
4

写入get_last,提取参数包的最后一个元素。

调用它f。打电话f

举个例子,

template<typename T0>
auto get_last( T0&& t0 )->decltype(std::forward<T0>(t0))
{
  return std::forward<T0>(t0);
}
template<typename T0, typename... Ts>
auto get_last( T0&& t0, Ts&&... ts )->decltype(get_last(std::forward<Ts>(ts)...))
{
  return get_last(std::forward<Ts>(ts)...);
}

如果您不关心重载解决方案,只需get_last像函子一样调用和对待它可能就足够了:

template <typename...Args>
void call(const char *name, Args...&& args)
{
    auto&& f = get_last(std::forward<Args>(args)...);
    f(3);
}

下一步是做一些 SFINAEenable_if魔术,以便在call最后没有传递有效函子的情况下使失败匹配:但是,这可能是矫枉过正。

为了检测是否f(3)会起作用,一个简单的特征类:

// trivial traits class:
template<typename T>
struct is_type:std::true_type {};

template<typename Functor, typename=void>
struct can_be_called_with_3:std::false_type {}

template<typename Functor>
struct can_be_called_with_3<Functor,
  typename std::enable_if<
    std::is_type< decltype(
      std::declval<Functor>(3)
    ) >::value
  >::type
>:std::true_type {}

这很愚蠢。如果您对传入类型的要求更复杂(例如,您希望使用参数调用它),则必须使用更高级的特征类。

然后你增加call

template <typename...Args>
auto call(const char *name, Args...&& args)
  -> typename std::enable_if<
       can_be_called_with_3< decltype( get_last(std::forward<Args>(args)... ) ) >::value
     >::type
{ /* body unchanged */ }

这很钝。

于 2013-04-12T17:50:07.663 回答
0

如果您想将一组参数作为模板,您不能以您已经做过的方式编写它:

template <typename...Args, typename Func>
void call(const char *name, Args...args, Func f)
{
        f(3);
}

但是你可以把它们打包成一个std::tuple

template <typename...Args, typename Func>
void call(const char *name, std::tuple<Args...> args, Func f)
{
        f(3);
}

call("test", std::forward_as_tuple(1, 2, 3), [=](int i) { std::cout<< i; });
于 2013-04-12T18:03:24.260 回答