模板类模板构造函数专用化

Template class template constructor specialization

本文关键字:专用 构造函数      更新时间:2023-10-16

我有一个模板类,它有一个成员,其类型取决于类的模板参数。该类有一个模板构造函数。如何为类的模板参数的不同情况专门化构造函数,以确定所述成员的类型。有问题的不同类型是具有不同构造函数签名的类,我想在初始值设定项列表中调用成员的构造函数。有变通办法吗?我希望避免使用工厂函数,因为我不想依赖于成员的move构造函数是廉价的。

编辑:下面是一个代码示例,

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,我想默认初始化。

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

同样,如果没有一个例子,很难知道你做了什么,不理解什么。但一般来说,这是以与专门化其他方法相同的方式完成的。在头中声明所有的专门化,并在实现文件中实现它们(除非它们是部分专门化!)。专业化时请记住使用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特别提到了它的构造。

按照聚合初始化的规则初始化数组

然后聚合初始化会这样说。

如果初始值设定项子句的数量小于成员的数量或初始值设定项列表完全为空,则剩余成员为值初始化

最后,值初始化说明了这一点。

1)如果T是一个类类型,至少有一个用户提供的任何类型的构造函数,则调用默认构造函数;

4) 否则,对象初始化为零。

这允许我们执行std::array<T, 10> my_array{};,并有十个零或默认构造的Ts。我们也可以执行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;
};