2017-02-20 1 views
3

달성하고자하는 것은 세 개의 인수, 반복자 쌍 및 값을 변환하는 함수를받는 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에서 decltype 및/또는 std::remove_cv을 포장 할 수 있습니다

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

range는 [it] (https://ericniebler.github.io/std/wg21/D4128.html)과 비슷한 한 쌍의 반복자 대신 range 객체를 사용합니까? 아니면 다른 것이 있습니까? – qeatzy

+1

'decay_t'가 타입을 저장에 적합하게 만들기 때문에'std :: decay_t '을 사용할 것입니다. 두 번째 요점은'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

@ Barry가 지적했듯이, 아이디어는 비슷하지만 더 일반적인 R이 필요합니다. – qeatzy

관련 문제