2017-01-04 3 views
2

У меня есть класс шаблона с членом, тип которого зависит от аргумента шаблона класса. Класс имеет конструктор шаблона. Как я специализирую конструктор для разных случаев аргумента шаблона класса, который определяет тип указанного элемента. Различные типы, о которых идет речь, это классы с разными сигнатурами конструктора, и я хотел бы вызвать конструктор элемента в списке инициализаторов. Есть ли обходной путь? Я хотел бы избегать использования заводских функций, потому что я не хочу полагаться на то, что конструктор этого элемента дешев.Специалист по шаблону шаблона шаблона

Edit: Вот пример кода,

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*/) 
    {} 
}; 

Моя цель состоит в том, чтобы правильно инициализировать container независимо от того, является Cstd::vector или std::array. Если std::is_arithmetic<value_type>::value==true, я хочу инициализировать контейнер со всеми нулями (здесь возникает проблема с различными сигнатурами конструктора). Если 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) В противном случае объект инициализируется нулем.

Это позволит нам сделать это std::array<T, 10> my_array{}; и иметь десять обнуляется или по умолчанию построены T с. Мы также можем сделать std::vector<T> my_vector(10, T{});, чтобы получить тот же результат (T{} is value built`).

Редактировать 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

Смежные вопросы