模板类模板构造函数特化

时间:2017-01-04 21:40:07

标签: c++ templates constructor c++14 specialization

我有一个模板类,其成员的类型取决于类'模板参数。该类有一个模板构造函数。如何针对类的不同情况专门构造构造函数?模板参数,用于确定所述成员的类型。有问题的不同类型是具有不同构造函数签名的类,我想在初始化列表中调用成员的构造函数。有解决方法吗?我想避免诉诸工厂职能,因为我不想依赖廉价成员的移动构造函数。

修改: 这是一个代码示例,

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

无论containerC还是std::vector,我的目标都是正确初始化std::array。如果是std::is_arithmetic<value_type>::value==true,我想用全零来初始化容器(这是不同构造函数签名的问题所在)。如果std::is_arithmetic<value_type>::value==false,我想默认初始化。

1 个答案:

答案 0 :(得分:3)

如果没有示例,您的问题有点难以理解。我对它的理解是你想要专门化类模板的构造函数,并为不同的模板参数构造它的成员。如果这不正确,请告诉我,我会调整我的答案。

再一次没有例子,很难知道你做了什么,不明白。但通常这与您专门研究其他方法的方式相同。声明标头中的所有特化并在实现文件中实现它们(除非它们是部分特化!)。请记住在专业化时使用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{}是值构造的。)。

编辑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;
};