成员变量如何使用专用类模板?

How do member variables work with specialized class templates?

本文关键字:专用 变量 何使用 成员      更新时间:2023-10-16

我正在尝试编写一个非常简单的专用类模板,该模板具有成员变量,并且可以在特殊情况下以不同的方式打印该成员变量。我知道这个例子毫无用处,但它很好地说明了这个问题。

当专用类模板时,似乎类的专用化不共享相同的成员变量,因此以下代码不会编译...

#include <iostream>
#include <string>
// Class template
template <typename T>
struct S
{
S(const T& t)
: t(t)
{}
void print()
{
std::cout << t << std::endl;
}
private:
T t;
};
// Specialization
template <>
struct S<std::string>
{
void print()
{
// ERROR: "t" is not defined in this context
std::cout << "string: " << t << std::endl;
}
};

这表明我需要为每个专用化编写一个单独的构造函数,并为每个专用化编写一个单独的成员变量t,如果我有很多专业化,感觉很快就会变成大量重复的代码和工作。

如果我说的是真的,那么在专门的类模板中完全使用成员变量是不好的做法吗?是否有任何替代方法可以减少代码重复?

也请看@0x499602D2的答案,它更简单,适用于许多实际案例。

你是对的,专业基本上完全独立于彼此和原始模板,所以你必须写所有新的东西。解决这个问题的一种方法是使用继承。

#include <iostream>
#include <string>
// Class template
template <typename T>
struct Base
{
Base(const T& t)
: t(t)
{}
virtual void print()
{
std::cout << t << std::endl;
}
protected:
T t;
};
template<class T>
struct S: Base<T> {
};
// Specialization
template <>
struct S<std::string>: Base<std::string>
{
void print() override
{
std::cout << "string: " << t << std::endl;
}
};

由于您只专用于单个模板参数,因此您可以显式专用于成员函数而不是整个类:

template <>
void S<std::string>::print()
{
std::cout << "string: " << t << std::endl;
}

另一种可能的解决方案是标签分散

template <typename T>
struct S
{
private:
T t;
void print_helper (std::true_type) // T is std::string
{ std::cout << "string: " << t << std::endl; }
void print_helper (std::false_type) // T isn't std::string
{ std::cout << t << std::endl; }
public:
S (T const & t0) : t{t0}
{ } 
void print ()
{ print_helper(std::is_same<T, std::string>{}); }
};

另一种方法是使用辅助函数。 这将允许您进行部分模板专用化,解决@0x499602D2指出的问题。 我们正在做的是让模板化函数调用帮助程序函数,而帮助程序函数正在执行所有专用化。

我在其中添加了另一个模板参数,以表明此解决方案适用于部分模板专用化。 请注意,模板化帮助程序函数是完全专用的,而不是部分专用的。 您不能部分专化函数。 这在类模板具有更多模板参数而您无法专用(UNUSED_T(但您想要专用的函数可以完全专用(print_it不需要UNUSED_T(的情况下很有用。

#include <iostream>
#include <string>
// This is the helper function for all types T...
template <typename T>
void print_it(T t) {
std::cout << t << std::endl;
}
// ... except for std::string, it will run this one.
template <>
void print_it<std::string>(std::string t) {
std::cout << "string: " << t << std::endl;
}
// Class template, UNUSED is there just to show that
// this works for partial template specialization.
template <typename T, typename UNUSED_T>
struct S {
S(const T& t) : t(t) {}
void print() {
// You can remove the <T> because
// the compiler will figure it out for you.
print_it<T>(t);
}
prviate:
T t;
UNUSED_T unused;
};
int main() {
S<uint, char> x(5);
x.print(); // OUTPUT: 5
S<std::string, char> y("foo");
y.print(); // OUTPUT: string: foo
}