【问题标题】:template argument type deduction from std::function return type with lambda使用 lambda 从 std::function 返回类型推导模板参数类型
【发布时间】:2012-09-13 11:21:02
【问题描述】:

首先,我使用的是 C++11(而且我的话题很烂)。

我要做的是编写一个通用模板函数,该函数实现在其他编程语言中通常称为sort_by 的东西。它涉及为范围的每个成员只计算一次任意标准,然后根据这些标准对该范围进行排序。这样的标准不一定是 POD,它必须是不可比的。对于 std::less 不起作用的事情,调用者应该能够提供她自己的比较函子。

我已经成功编写了使用以下签名的函数:

template<  typename Tcriterion
         , typename Titer
         , typename Tcompare = std::less<Tcriterion>
         >
void
sort_by(Titer first, Titer last,
        std::function<Tcriterion(typename std::iterator_traits<Titer>::value_type const &)> criterion_maker,
        Tcompare comparator = Tcompare()) {
}

它可以用于例如像这样:

struct S { int a; std::string b; double c; };
std::vector<S> s_vec{
  { 42, "hello", 0.5 },
  { 42, "moo!",  1.2 },
  { 23, "fubar", 0.2 },
};

sort_by1< std::pair<int, double> >(
  s_vec.begin(), s_vec.end(),
  [](S const &one_s) { return std::make_pair(one_s.a, one_s.c); }
);

我不喜欢这种方法的是我必须自己提供Tcriterion 参数,因为编译器无法从 lambda 表达式中推断出该类型。因此这不起作用:

sort_by1(s_vec.begin(), s_vec.end(), [](S const &one_s) { return std::make_pair(one_s.a, one_s.c); });

clang 3.1 和 gcc 4.7.1 都对此大喊大叫(gcc 4.7.1 甚至 barks on the code above,所以我想我真的在这里做错了)。

但是,如果我首先将 lambda 分配给 std::function,那么至少 clang 3.1 可以推断出参数,这意味着它有效:

typedef std::pair<int, double> criterion_type;
std::function<criterion_type(S const &)> criterion_maker = [](S const &one_s) {
  return std::make_pair(one_s.a, one_s.c);
};
sort_by1(s_vec.begin(), s_vec.end(), criterion_maker);

所以我的问题是:我如何更改我的函数签名,以便我不需要指定一个参数?并且(可能相关)我将如何修复我的示例以使其与 gcc 一起使用?

【问题讨论】:

标签: c++ c++11


【解决方案1】:

不要将std::function 与模板参数推导一起使用。事实上,很可能没有理由在函数或函数模板参数列表中使用std::function。很多时候,你不应该使用std::function;它是一种非常专业的工具,非常擅长解决一个特定问题。其余时间,您可以完全省去它。

在您的情况下,如果您使用多态函子来订购东西,则不需要模板参数推导:

struct less {
    template<typename T, typename U>
    auto operator()(T&& t, U&& u) const
    -> decltype( std::declval<T>() < std::declval<U>() )
    { return std::forward<T>(t) < std::forward<U>(u); }

    // operator< is not appropriate for pointers however
    // the Standard defines a 'composite pointer type' that
    // would be very helpful here, left as an exercise to implement
    template<typename T, typename U>
    bool operator()(T* t, U* u) const
    { return std::less<typename std::common_type<T*, U*>::type> {}(t, u); }
};

然后你可以声明:

template<typename Iter, typename Criterion, typename Comparator = less>
void sort_by(Iter first, Iter last, Criterion crit, Comparator comp = less {});

comp(*ita, *itb) 会做正确的事情,comp(crit(*ita), crit(*itb)) 或其他任何事情只要有意义。

【讨论】:

  • 虽然这看起来不错,但 clang 3.1 和 gcc 4.7.1 都无法推断出 Comparator 的调用,就像我在原始示例中发布的那样:sort_by(v.begin(), v.end() [](...) { return ...; });
  • @MoritzBunkus 应该这样做。
  • 明确地说,您的尝试无法挽救。该语言的规则是无法推断出TCriterion,除非您传入std::function。
【解决方案2】:

这样的事情怎么样:

template<  typename Titer
         , typename Tmaker
         , typename Tcompare
         >
void
sort_by(Titer first, Titer last,
        Tmaker criterion_maker,
        Tcompare comparator)
{
  typedef decltype(criterion_maker(*first)) Tcriterion;
  /*
    Now that you know the actual type of your criterion,
    you can do the real work here
  */
}

问题是你显然不能使用这个比较器的默认值,但你可以通过提供一个不带比较器并在内部填充std::less 的重载来轻松克服这个问题。

要按照您最初的建议进行操作,编译器必须能够“反​​转”模板实例化过程。 IE。对于给定的 std::function 实例化,我必须提供什么参数作为结果才能得到它。这“看起来”很简单,其实不然!

【讨论】:

  • 我想过这样做,而 stdlib 似乎也一直这样做,但我不确定这是否是原因。因此,如果不可能,那么我很乐意接受您的回答。
  • 原因是你不能让编译器反转一般的元函数。
【解决方案3】:

你也可以使用类似的东西。

     template<  typename Titer
     , typename Tmaker
     , typename TCriterion = typename
     std::result_of
     <
      Tmaker
      (
        decltype(*std::declval<Titer>()) 
      )
     >::type
     , typename Tcompare = std::less<TCriterion>
     >
void
sort_by(Titer first, Titer last,
        Tmaker criterion_maker, Tcompare comparator = Tcompare())
{
}

http://liveworkspace.org/code/0aacc8906ab4102ac62ef0e45a37707d

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-24
    • 2015-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多