模板参数类型从std::函数返回类型与lambda。

时间:2020-12-22 16:33:26

First of, I'm using C++11 (and my topic sucks).

首先,我使用c++ 11(我的主题很烂)。

What I'm trying to do is write a generic template function that implements something usually called sort_by in other programming languages. It involves calculating an arbitrary criterion for each member of a range exactly once and then sorting that range according to those criteria. Such a criterion doesn't have to be a POD, all it has to be is less-than-comparable. For things for which std::less doesn't work the caller should be able to provide her own comparison functor.

我要做的是编写一个通用模板函数,它实现了其他编程语言中通常称为sort_by的东西。它包括精确地计算一个范围内每个成员的任意标准,然后根据这些标准对该范围进行排序。这样的标准不需要是一个豆荚,它必须是不可比性的。对于std::less不起作用的东西,调用者应该能够提供她自己的比较函数。

I've successfully written said function which uses the following signature:

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

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()) {
}

It can be used e.g. like this:

可以这样使用:

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); }
);

What I don't like about this approach is that I have to provide the Tcriterion argument myself because the compiler cannot deduce that type from the lambda expression. Therefore this does not work:

我不喜欢这种方法的原因是,我自己必须提供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 and gcc 4.7.1 both bark on this (gcc 4.7.1 even barks on the code above, so I guess I'm really doing something wrong here).

clang 3.1和gcc 4.7.1都是这样的(gcc 4.7.1甚至在上面的代码上都是如此,所以我想我在这里确实做错了)。

However, if I assign the lambda to a std::function first then at least clang 3.1 can deduce the argument, meaning this works:

但是,如果我将lambda赋值给std::函数首先,那么至少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);

So my questions are: How do I have to change my function signature so that I don't need to specify that one argument? And (probably related) how would I fix my example to have it working with gcc?

所以我的问题是:我如何改变我的函数签名以便我不需要指定一个参数?并且(可能是相关的)我如何修正我的例子,让它与gcc一起工作?

3 个解决方案

#1


8  

Don't use std::function in tandem with template argument deduction. In fact, there's very likely no reason to use std::function in a function or function template argument list. More often than not, you should not use std::function; it is a very specialized tool that is very good at solving one particular problem. The rest of the time, you can dispense with it altogether.

不要使用std::功能与模板参数推导。事实上,很可能没有理由使用std::函数在函数或函数模板参数列表中。通常情况下,您不应该使用std::函数;它是一个非常专业的工具,非常擅长解决一个特定的问题。剩下的时间,你可以完全放弃。

In your case you don't need template argument deduction if you use a polymorphic functor to order things:

在你的情况下,如果你使用一个多态函数来排序,你不需要模板参数推导:

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); }
};

You can then declare:

然后您可以声明:

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

and comp(*ita, *itb) will do the right thing, as well as comp(crit(*ita), crit(*itb)) or anything else as long as it makes sense.

而comp(*ita, *itb)将会做正确的事情,以及comp(crit(*ita), crit(*itb))或任何其他的东西,只要它有意义。

#2


3  

How about something like this:

比如这样:

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
  */
}

The problem is that you can obviously not use a default for the comparator with this, but you can easily overcome that by providing an overload that doesn't take a comparator and fills in std::less internally.

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

To do it like you originally suggested, the compiler would have to be able to "invert" the template instantiation process. I.e. for a given std::function<> instantiation, what parameter do I have to supply as the result to get it. This "looks" easy, but it is not!

要像您最初建议的那样执行,编译器必须能够“反转”模板实例化过程。例如,对于给定的std::函数<>实例化,我必须提供什么参数来得到它。这看起来很简单,但事实并非如此!

#3


1  

You can use also something like this.

你也可以用类似的方法。

     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

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

#1


8  

Don't use std::function in tandem with template argument deduction. In fact, there's very likely no reason to use std::function in a function or function template argument list. More often than not, you should not use std::function; it is a very specialized tool that is very good at solving one particular problem. The rest of the time, you can dispense with it altogether.

不要使用std::功能与模板参数推导。事实上,很可能没有理由使用std::函数在函数或函数模板参数列表中。通常情况下,您不应该使用std::函数;它是一个非常专业的工具,非常擅长解决一个特定的问题。剩下的时间,你可以完全放弃。

In your case you don't need template argument deduction if you use a polymorphic functor to order things:

在你的情况下,如果你使用一个多态函数来排序,你不需要模板参数推导:

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); }
};

You can then declare:

然后您可以声明:

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

and comp(*ita, *itb) will do the right thing, as well as comp(crit(*ita), crit(*itb)) or anything else as long as it makes sense.

而comp(*ita, *itb)将会做正确的事情,以及comp(crit(*ita), crit(*itb))或任何其他的东西,只要它有意义。

#2


3  

How about something like this:

比如这样:

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
  */
}

The problem is that you can obviously not use a default for the comparator with this, but you can easily overcome that by providing an overload that doesn't take a comparator and fills in std::less internally.

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

To do it like you originally suggested, the compiler would have to be able to "invert" the template instantiation process. I.e. for a given std::function<> instantiation, what parameter do I have to supply as the result to get it. This "looks" easy, but it is not!

要像您最初建议的那样执行,编译器必须能够“反转”模板实例化过程。例如,对于给定的std::函数<>实例化,我必须提供什么参数来得到它。这看起来很简单,但事实并非如此!

#3


1  

You can use also something like this.

你也可以用类似的方法。

     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

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