2015-01-14 3 views
20

각 템플릿 인수는 내부 계산이 처리 할 수있는 한 가지 유형의 값을 나타냅니다. 값이 boost :: any로 전달되고 런타임 전에 해당 유형이 명확하지 않기 때문에 (함수 오버로드 대신) 템플리트가 필요합니다.가변 인수 당 하나의 클래스 멤버 생성하기 템플릿 인수

template<typename ...AcceptedTypes> // e.g. MyClass<T1, T2> 
class MyClass { 
    std::vector<T1> m_argumentsOfType1; 
    std::vector<T2> m_argumentsOfType2; // ... 
}; 

또는 양자 택일로, 나는 템플릿 인수를 보관하고 싶습니다 :

가 제대로 올바른 유형으로 캐스팅하기 위해, 나는 이런 식으로 뭔가를 각 가변 인수 유형에 대한 멤버 목록을 가지고 싶습니다 목록에 RTTI 마술을 할 때 (?) 그러나 std :: initializer_list 멤버에 저장하는 방법 또한 나에게 불분명하다.

도움 주셔서 감사합니다.

+2

을 (http://en.cppreference.com/w/cpp/utility/tuple). –

+0

별도의 멤버 여야합니까, 아니면 벡터 모음을 가지고 있을까요? 예 : 벡터의'std :: array'? [어느 쪽이든 해결하는 방법을 모르지만이 질문에 대한 답은이를 해결하는 방법을 알고있는 사람들을 도울 것입니다.] –

+0

컬렉션도 좋습니다. 그게 도움이 될지 확신 할 수는 없지만. 어떤 시점에서 컨테이너 (std :: vector)는 형식을 알아야합니다. – user1101674

답변

11

는 가장 좋은 방법은 튜플을 사용하는 것입니다. 또 다른 중요한 점은 그들에게 명명 된 접근을하는 것일 수도 있습니다.나는 당신이 달성하려고하는 것은 독특한 유형의 여러 벡터를 가지고하는 것입니다 생각, 그래서 당신은 그 값 유형에 따라 정확한 벡터에 대해 "검색"할 수있는 다음과 같은 기능이 있습니다 :

다음
template <class T1, class T2> 
struct SameType 
{ 
    static const bool value = false; 
}; 

template<class T> 
struct SameType<T, T> 
{ 
    static const bool value = true; 
}; 

template <typename... Types> 
class MyClass 
{ 
    public: 
    typedef std::tuple<vector<Types>...> vtype; 
    vtype vectors; 

    template<int N, typename T> 
    struct VectorOfType: SameType<T, 
     typename std::tuple_element<N, vtype>::type::value_type> 
    { }; 

    template <int N, class T, class Tuple, 
       bool Match = false> // this =false is only for clarity 
    struct MatchingField 
    { 
     static vector<T>& get(Tuple& tp) 
     { 
      // The "non-matching" version 
      return MatchingField<N+1, T, Tuple, 
        VectorOfType<N+1, T>::value>::get(tp); 
     } 
    }; 

    template <int N, class T, class Tuple> 
    struct MatchingField<N, T, Tuple, true> 
    { 
     static vector<T>& get(Tuple& tp) 
     { 
      return std::get<N>(tp); 
     } 
    }; 

    template <typename T> 
    vector<T>& access() 
    { 
     return MatchingField<0, T, vtype, 
       VectorOfType<0, T>::value>::get(vectors); 
    } 
}; 

인을 테스트 케이스 당신이 그것을 시도 할 수 있습니다 : 당신은 당신이 MyClass의 전문에 전달 유형의 목록에없는 모든 유형을 사용하는 경우

int main(int argc, char** argv) 
{ 
    int twelf = 12.5; 
    typedef reference_wrapper<int> rint; 

    MyClass<float, rint> mc; 
    vector<rint>& i = mc.access<rint>(); 

    i.push_back(twelf); 

    mc.access<float>().push_back(10.5); 

    cout << "Test:\n"; 
    cout << "floats: " << mc.access<float>()[0] << endl; 
    cout << "ints: " << mc.access<rint>()[0] << endl; 
    //mc.access<double>(); 

    return 0; 
} 

가 (더블이 주석 처리 된 액세스 참조), 당신은 얻을 것이다 컴파일 에러, 너무 읽기 어렵지는 않지만 gcc는 적어도 문제를 야기한 정확한 위치를 가리키고 적어도 그러한 에러 메시지는 제안한다. 문제의 정확한 원인 - 여기, 예를 들어, 당신은 mc.access < 더블 >()하려고 노력하는 경우 : 당신은 [`표준 : tuple`]에 인수 유형을 전달할 수 있습니다

error: ‘value’ is not a member of ‘MyClass<float, int>::VectorOfType<2, double>’ 
+0

지금까지 사용해 주셔서 감사합니다. 그러나 대신 벡터 >을 원한다면 어떻게해야합니까? 다음 오류가 발생합니다 : 오류 C2504 : 'std :: tuple_element <0, std :: tuple <>>': 기본 클래스가 정의되지 않았습니다. 클래스 템플릿 인스턴스화에 대한 참조를 참조하십시오 'std :: tuple_element <1, std :: tuple >, std :: allocator >>>>> '컴파일 된 입니다. .. – user1101674

+0

'#include '하고 C++ 11 모드로 컴파일 했습니까? 필자의 테스트 케이스를 약간 변경했고,'reference_wrapper '를 사용하여'int' 변수를 밀어 넣었습니다. 모든 것이 정상적으로 작동합니다. 나는 당신이 그것을 볼 수 있도록 전체 testcase 함수를 넣을 것이다. – Ethouris

+0

고마워, 작동 해! 컴파일러 오류를 유발 한 원인은 위의 Richard Hodges가 자동으로 boost :: any의 벡터를 해당 유형의 멤버 벡터에 삽입하려고 시도한 몇 가지 코드가 있다는 것입니다. – user1101674

4

πάντα-ῥεῖ의 설명에서 언급 한 것과 같은 한 가지 방법은 튜플을 사용하는 것입니다. 그가 설명하지 않은 것 (아마 너 자신을 구하기 위해)은 어떻게 보일지 모른다. 여기

은 예이다 :

using namespace std; 

// define the abomination  
template<typename...Types> 
struct thing 
{ 
    thing(std::vector<Types>... args) 
    : _x { std::move(args)... } 
    {} 

    void print() 
    { 
     do_print_vectors(std::index_sequence_for<Types...>()); 
    } 

private: 
    template<std::size_t... Is> 
    void do_print_vectors(std::index_sequence<Is...>) 
    { 
     using swallow = int[]; 
     (void)swallow{0, (print_one(std::get<Is>(_x)), 0)...}; 
    } 

    template<class Vector> 
    void print_one(const Vector& v) 
    { 
     copy(begin(v), end(v), ostream_iterator<typename Vector::value_type>(cout, ",")); 
     cout << endl; 
    } 

private: 
    tuple<std::vector<Types>...> _x; 
}; 


// test it 
BOOST_AUTO_TEST_CASE(play_tuples) 
{ 
    thing<int, double, string> t { 
     { 1, 2, 3, }, 
     { 1.1, 2.2, 3.3 }, 
     { "one"s, "two"s, "three"s } 
    }; 

    t.print(); 
} 

예상 출력 :

template<typename ... Ts> 
using variant_vector = boost::variant< std::vector<Ts>... >; 

template<typename ...Ts> 
struct MyClass { 
    using var_vec = variant_vector<Ts...>; 
    std::array<var_vec, sizeof...(Ts)> vecs; 
}; 

우리 변이체 벡터를 생성 : 여기

1,2,3, 
1.1,2.2,3.3, 
one,two,three, 
+0

아, 벡터 인수를 가져 오는 여분의 생성자를 놓쳤습니다. 나는'std :: tuple >'의 이니셜 라이저 목록을 뒤적 거리며이 생성자의 명백한 속성을 다루었 다. – TemplateRex

+0

재미있는 운동이지만 생산 코드에서 이것을 보았다면 누군가를 해고하려고합니다 :-) –

+1

운동의 유형은 이점을 가질 수 있습니다. 데이터 지향 디자인에서는 더 나은 캐싱 (caching) 때문에'std :: vector '대신에'std :: tuple '을 자주 보게됩니다. 그러나 데이터 레이아웃의 상단에 후자의 인터페이스를 제공하는 것이 편리 할 수 ​​있습니다. 전자 그러한 변환을 손으로 코딩하는 것을 피하려면 일부 가변 튜플 매직이 유용 할 수 있습니다! – TemplateRex

0

boost::variant를 사용하여보다 완벽 효율적인 구현 그 안에 유형 목록 중 하나를 보유 할 수 있습니다. 콘텐츠를 얻으려면 boost::variant을 사용해야합니다 (즉, 콘텐츠의 유형을 알고 있거나 방문자를 쓰는 것을 의미합니다).

그런 다음 유형별로 하나씩이 변형 벡터의 배열을 저장합니다.

이제 클래스에 한 가지 유형의 데이터 만있는 경우 배열을 제거하고 var_vec 유형의 멤버가 하나만있을 수 있습니다.

각 유형마다 하나의 벡터가 필요한 이유를 알 수 없습니다. 각 요소가 모든 유형 중 하나 인 벡터를 원할 수 있습니다. 위의 variant<vector<Ts>...>과 반대로 이는 vector<variant<Ts...>>입니다.

variant<Ts...>boost입니다. anyboost 스마트-void*입니다. optionalboost입니다.

template<class...Ts> 
boost::optional<boost::variant<Ts...>> to_variant(boost::any); 

any를 취하고 variantTs... 유형의로 변환을 시도하고 반환을이 성공하면 (그리고 빈 optional없는 경우를 반환) 유용한 기능 일 수있다.

template<typename ...AcceptedTypes> // e.g. MyClass<T1, T2> 
class MyClass { 
    std:tuple<std::vector<AcceptedTypes>...> vectors; 
}; 

이것은 당신이 마술은 필드 이름을 철자 할 수 없기 때문에 "필드"를 곱 수있는 유일한 방법입니다 : 당신이 이미 암시 된 것처럼