2017-01-04 2 views
2

클래스 템플릿 인수에 따라 형식이 다른 멤버가있는 템플릿 클래스가 있습니다. 클래스에는 템플릿 생성자가 있습니다. 어떻게하면 해당 멤버의 유형을 결정하는 클래스의 템플릿 인수의 여러 경우에 대해 생성자를 전문화 할 수 있습니까? 문제의 다른 유형은 생성자 서명이 다른 클래스이며, 이니셜 라이저 목록에서 멤버의 생성자를 호출하고 싶습니다. 해결 방법이 있습니까? 값싼 멤버의 이동 생성자에 의존하기를 원하지 않기 때문에 공장 함수를 사용하지 않는 것이 좋습니다.템플릿 클래스 템플릿 생성자 전문화

편집 : 는 여기

template <typename T, typename C> 
struct foo { 
    T m; 
    C container; 
    using value_type = typename C::value_type; 

    template <typename... Args> 
    foo(Args&&... args) 
    : m(std::forward<Args>(args)...), container(/*different stuff here*/) 
    {} 
}; 

내 목표는 제대로 여부에 관계없이 Ccontainer를 초기화하는 것입니다, std::vector 또는 std::array A A 코드 예입니다. std::is_arithmetic<value_type>::value==true 인 경우 컨테이너를 모두 0으로 초기화하려고합니다 (여기서는 생성자 서명이 다른 문제가 발생합니다). std::is_arithmetic<value_type>::value==false 인 경우 기본값을 초기화하려고합니다.

+1

코드 샘플은 문제를 자세히 설명합니다. 이런 식으로 뭔가를 찾으십니까? - http://coliru.stacked-crooked.com/a/5d9ea40421963b70? – Praetorian

+0

기회를 얻 자마자 제공 한 예를 사용하여 답변을 수정하겠습니다. –

답변

2

예제가 없으면 이해하기가 약간 까다 롭습니다. 그것을 이해하면 클래스 템플릿의 생성자를 전문화하고 다른 템플릿 인수에 대해 멤버를 다르게 구성하려고합니다. 이것이 올바르지 않으면 알려 주시면 답변을 조정하겠습니다.

다시 한번 예를 들자면 당신이하는 일이나 이해하지 못한 것을 알기가 어렵습니다. 그러나 일반적으로 이것은 다른 방법을 전문으로하는 것과 같은 방식으로 수행됩니다. 헤더의 모든 전문화를 선언하고 구현 파일에 구현하십시오 (부분 전문화가 아닌 경우). 전문화 할 때는 template<>을 사용해야합니다. 예를 들면 다음과 같습니다.

struct t_param_for_int {}; 
struct t_param_for_double {}; 


// t_member's constructor is very different depending on T 
template<class T> struct t_member {}; 

template<> struct t_member<int> { 
    explicit t_member(const t_param_for_int value) {}; 
}; 

template<> struct t_member<double> { 
    explicit t_member(const t_param_for_double value) {}; 
}; 

// Foo has a t_member and a constructor 
template<class T> 
struct foo 
{ 
    foo(); 

    t_member<T> member; 
}; 

// Declare specialization 
template<> foo<int>::foo(); 
template<> foo<double>::foo(); 

// Specialization implementations (in a .cpp) 
template<> foo<int>::foo() : member(t_param_for_int{}) 
{ } 

// Specialization implementations (in a .cpp) 
template<> foo<double>::foo() : member(t_param_for_double{}) 
{ } 

int main() 
{ 
    foo<int> int_foo; 
    foo<double> dbl_foo; 
    return 0; 
} 

편집 : 질문 편집에 대한 응답입니다.

이 경우 생성자를 전문으로 설정할 수 없습니다. 가장 좋은 해결책은 헬퍼 구조체를 사용하여 실제 초기화를 수행하는 것입니다. 당신은 어떤 숫자가 0이거나 디폴트 구성 요소 인 T으로 컨테이너를 초기화하기를 원한다고 언급했다. 그러나 컨테이너의 크기를 지정하지 않았습니다. 가짜 컨테이너 크기를 사용하여 도우미 구조체를 사용하는 방법을 보여주는 예를 만들었습니다.

#include <array> 
#include <iostream> 
#include <type_traits> 
#include <string> 
#include <utility> 
#include <vector> 

template<typename T, typename C> 
struct helper_init; 

template<typename T> 
struct helper_init<T, std::vector<T>> { 
    static std::vector<T> init() { 
     return std::vector<T>(3, T{}); // init your vector with 3 elements 
    } 
}; 

template<typename T> 
struct helper_init<T, std::array<T, 2>> { 
    static std::array<T, 2> init() { 
     return {}; // init your array with 2 elements 
    } 
}; 

template <typename T, typename C> 
struct foo { 
    T m; 
    C container; 
    using value_type = typename C::value_type; 

    template <typename... Args> 
    foo(Args&&... args) 
    : m(std::forward<Args>(args)...) 
    , container(helper_init<T, C>::init()) 
    {} 
}; 

int main() 
{ 
    foo<int, std::vector<int>> vec_foo(5); 
    foo<std::string, std::array<std::string, 2>> str_foo("hello"); 

    // Output to illustrate 

    // The size of the containers are 3 and 2 (see container initialization) 
    std::cout << vec_foo.container.size() << ' ' 
     << str_foo.container.size() << std::endl; 

    // The m members contain the assigned values 
    std::cout << vec_foo.m << " \'" << str_foo.m << '\'' << std::endl; 

    // The containers are zero or default initialized 
    std::cout << vec_foo.container.front() << " \'" << 
     str_foo.container.front() << '\'' << std::endl; 
    return 0; 
} 

산술 형식을 0으로 초기화하고 클래스 형식을 기본으로 구성하는 질문의 두 번째 부분에 대해서는 언어에 이미이 기능이 있습니다.

std::array 구체적으로 말하면 건설에 대해 말합니다.

그런 다음 aggregate initialization이 말한다 집계 초기화의 규칙에 따라 배열을 초기화.

초기화 조항의 수는 회원 또는 초기화 목록의 수보다 작 으면

완전히 비어, 나머지 멤버는 값 초기화을.

마지막으로 value initialization은 이것을 말합니다.

1) T가 모든 종류의 사용자 제공 생성자가 하나 이상있는 클래스 유형 인 경우 기본 생성자가 호출됩니다.

4) 그렇지 않으면 개체가 0으로 초기화됩니다.

이렇게하면 std::array<T, 10> my_array{};이되며 0이 지정되거나 기본값 인 T이 생성됩니다. std::vector<T> my_vector(10, T{});도 같은 결과를 얻을 수 있습니다 (T{} is value constructed`).

편집 2 : [위임 생성자] 및 태그 디스 패칭을 사용하여 질문 요구 사항을보다 잘 준수하는 또 다른 솔루션이 있습니다.

#include <array> 
#include <string> 
#include <vector> 


// Tags for supported containers 
struct t_tag_array {}; 
struct t_tag_vector {}; 

// Tag dispatching 
template<class T, size_t S> 
struct t_container_tag {}; 

template<class T, size_t S> 
struct t_container_tag<std::vector<T>, S> { 
    using type = t_tag_vector; 
}; 

template<class T, size_t S> 
struct t_container_tag<std::array<T, S>, S> { 
    using type = t_tag_array; 
}; 

// Helper to fetch the size of an std::array 
template<typename> 
struct array_size; 

template<typename T, size_t S> 
struct array_size<std::array<T, S>> { 
    static const auto value = S; 
}; 

template <typename C, size_t S = array_size<C>::value> 
struct foo 
{ 
    using value_type = typename C::value_type; 

    // Constructor 
    template<typename... Args> 
    foo(Args&&... args) : foo(typename t_container_tag<C, S>::type{}, std::forward<Args>(args)...) {} 

    // Specialized constructor for vectors 
    template<typename... Args> 
    foo(t_tag_vector &&, Args&&... args) : m(std::forward<Args>(args)...), container(S, value_type{}) {} 

    // Specialized constructor for arrays 
    template<typename... Args> 
    foo(t_tag_array &&, Args&&... args) : m(std::forward<Args>(args)...), container{} {} 

    value_type m; 
    C container; 
}; 
+1

힌트 : 생성자 및 태그 디스 패칭을 위임합니다. 또는 SFINAE, 추가 더미 템플릿 매개 변수 사용) 생성자 인스턴스화 지연. – bogdan

+2

@bogdan 힌트를 기반으로 몇 가지 연구를 해봤습니다. 예제를 생각해 냈습니다 (아주 좋은 힌트입니다!). 나는 답변으로 게시 할 수있을만큼 자신감이 부족하다고 생각하지만이 의견의 맥락에서 의견을 제시하고자합니다. 내가 가진 것을 잠시 살펴보고 어떤 피드백을 돌려 주시겠습니까? [pastebin] (http://pastebin.com/34brLjeL) –

+0

그래, 잘된 일이야. – bogdan

관련 문제