Как переменные-члены работают со специализированными шаблонами классов?

Я пытаюсь написать очень простой специализированный шаблон класса, который имеет переменную-член и может печатать эту переменную-член по-разному в особых ситуациях. Я знаю, что пример довольно бесполезен, но он довольно хорошо иллюстрирует вопрос.

При специализации шаблонов классов кажется, что специализации класса не используют одни и те же переменные-члены, поэтому следующий код не будет компилироваться...

#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 для каждой специализации, которая кажется, что это быстро станет большим количеством дублированного кода и усилий, если у меня будет много специализаций.

Если то, что я говорю, верно, то является ли плохой практикой вообще использовать переменные-члены в специализированных шаблонах классов? Существуют ли какие-либо альтернативы, которые приводят к меньшему дублированию кода?


person tjwrona1992    schedule 27.12.2019    source источник


Ответы (3)


Пожалуйста, посмотрите также ответ @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;
    }
};
person n314159    schedule 27.12.2019

Поскольку вы специфицируете только один параметр шаблона, вы можете явно специфицировать функцию-член вместо всего класса:

template <>
void S<std::string>::print()
{
    std::cout << "string: " << t << std::endl;
}
person 0x499602D2    schedule 27.12.2019
comment
О, классно! Я никогда не думал об этом. Это не сработает для частичной специализации, верно? - person tjwrona1992; 27.12.2019
comment
@ tjwrona1992 Нет, это работает только для явных специализаций. - person 0x499602D2; 27.12.2019

Другим возможным решением является рассылка тегов.

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>{}); }
 };
person max66    schedule 27.12.2019