2017-02-20 7 views
3

私が達成したいのは、3つの引数、1組の反復子、および値を変換する関数を受け入れるmakeSet()関数です。 std::map<K,V>std::set<std::pair<V,K>>.関数テンプレートにlambdaを使用し、型を推定できません。makeSet()ユースケース

に、クライアントコードは、私の現在の試みとしてある

auto s = makeSet(hash.begin(), hash.end(), 
    [](std::pair<int,int> x) { return std::make_pair(x.second, x.first); }); 

のように見えるかもしれ

つのユースケースは、値のシーケンスからセットを作成することができ、変換は、例えば、変換しません続く、

// (commented code are some other *failed* attempt). 
template <typename Iterator, 
     typename T = typename std::iterator_traits<Iterator>::value_type, 
     template<typename ... > class Monad, typename R > 
     // typename R, typename Monad = std::function<R(T)> > 
std::set<R> makeSet(Iterator first, Iterator last, Monad<R,T> f) { 
    std::set<R> res; 
    for (; first != last; ++first) res.insert(f(*first)); 
    return res; 
} 

しかし、残念ながら動作しません。問題はRを推測できないようです。

解決策はありますか? 私はそれを行う正しい方法を教えていただければ幸いです。

答えて

9

ラムダ式の型は、無名のクラス型(クロージャ型)であり、std::functionではありません。したがって、std::functionまたはMonadを推論することはできません。

あなたの最善の策は、標準ライブラリが何をやっても、単に述語として何かを受け入れるだろう:あなたはすべてカバーするためにstd::remove_referenceおよび/またはstd::remove_cvdecltypeをラップする必要があり

template < 
    class Iterator, 
    class UnaryFunction 
> 
auto makeSet(Iterator first, Iterator last, UnaryFunction f) -> std::set<decltype(f(*first))> 
{ 
    std::set<decltype(f(*first))> res; 
    for (; first != last; ++first) res.insert(f(*first)); 
    return res; 
} 

注意コーナーケース(または、@Yakkのように、std::decay)。

また、車輪の再発明を避けるために、Boost.Rangeライブラリをご覧ください。

+0

ああ、いつも好きな宣言型! – qeatzy

+0

範囲は、[this](https://ericniebler.github.io/std/wg21/D4128.html)と似たイテレータのペアではなく、範囲のオブジェクトを使用することを意味しますか? – qeatzy

+1

私は単に 'std :: decay_t 'を使います。 'decay_t'は型を記憶に適したものにするためです。第2のポイントは、 'std :: function'の型を決して決して決めるべきではないということです。 'std :: function'は型消去クラスであり、型減算と型消去は反対です。消去するタイプを控除することは、設計上の欠陥の兆候です。ここでは固定タイプが必要なので消去します。あなたはここで正確なタイプを知っているから推測します。あなたが正確なタイプを知っていれば、999/1000回そこに固定タイプは必要ありません。 – Yakk

0

「配管をはじめるほど、排水を止めるのが簡単になります。」 - Scotty、Star Trek III。

テンプレート機能を過剰に設計する必要はありません。フォワーディングリファレンスを使用して、C++ 17コンパイラがすべてを理解できるようにしてください。

#include <set> 
#include <map> 
#include <utility> 
#include <type_traits> 

// (commented code are some other *failed* attempt). 
template <typename Iterator, typename Lambda> 
auto makeSet(Iterator first, Iterator last, Lambda &&f) { 

    typedef typename std::remove_reference<decltype(first->first)>::type const_first_t; 

    typedef typename std::remove_const<const_first_t>::type first_t; 

    typedef typename std::remove_reference<decltype(first->second)>::type second_t; 

    typedef std::pair<first_t, second_t> R; 


    std::set<R> res; 

    for (; first != last; ++first) res.insert(f(*first)); 
    return res; 
} 


void foo() 
{ 
    std::map<int, int> m; 

    std::set<std::pair<int, int>> s = 
     makeSet(m.begin(), m.end(), 
      [](const auto &x) 
      { 
       return std::make_pair(x.second, x.first); 
      }); 

} 
+1

'R'はここで書いたものではなく、' f(* first) 'の壊れた型であるはずです。 – Barry

+0

良い点!私はより簡潔なバージョンを好むだろうが。だから、私は似たような解決策を考え出すことができません。 – qeatzy

+0

@バリーが指摘したように、より一般的なRが必要ですが、アイデアは似ています。 – qeatzy

関連する問題