11

まず、私はC ++ 11を使用しています(そして私のトピックは最悪です)。

私がやろうとしているのはsort_by、他のプログラミング言語で通常呼び出されるものを実装するジェネリックテンプレート関数を作成することです。これには、範囲の各メンバーの任意の基準を1回だけ計算し、それらの基準に従ってその範囲を並べ替えることが含まれます。このような基準は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、コンパイラーがラムダ式からその型を推測できないため、自分で引数を指定する必要があることです。したがって、これは機能しません。

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

clang3.1とgcc4.7.1は両方ともこれに吠えます(gcc 4.7.1は上記のコードにも吠えるので、私はここで本当に何か間違ったことをしていると思います)。

ただし、ラムダを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);

だから私の質問は次のとおりです:その1つの引数を指定する必要がないように関数のシグネチャを変更するにはどうすればよいですか?そして(おそらく関連している)gccで動作するように例を修正するにはどうすればよいですか?

4

3 に答える 3

9

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

于 2012-09-13T11:38:23.070 に答える
3

このようなものはどうですか:

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内部で埋めるオーバーロードを提供することで簡単に克服できます。

最初に提案したようにするには、コンパイラはテンプレートのインスタンス化プロセスを「反転」できる必要があります。つまり、特定の std::function<> インスタンス化の場合、それを取得するために結果としてどのパラメーターを指定する必要がありますか。これは簡単に「見える」が、そうではない!

于 2012-09-13T11:33:14.917 に答える
1

このようなものも使用できます。

     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

于 2012-09-13T12:13:47.437 に答える