成员变量如何与专门的 class 模板一起使用?

How do member variables work with specialized class templates?

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

特化 class 模板时,class 的特化似乎不共享相同的成员变量,因此以下代码无法编译...

#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,如果我有,感觉它会很快变成大量重复的代码和工作许多专业。

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

请同时查看@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;
    }
};

由于您只特化了一个模板参数,因此您可以显式特化成员函数而不是整个 class:

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 指出的问题。我们正在做的是让模板化函数调用辅助函数,辅助函数执行所有专业化。

我在其中添加了另一个模板参数,以表明这种解决方案适用于部分模板专业化。请注意,模板化的辅助函数是完全特化的,而不是部分特化的。您不能部分特化一个函数。这在 class 模板具有更多您无法专门化的模板参数 (UNUSED_T) 但您 do 想要专门化的函数的情况下很有用可以完全专业化(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
}