c ++类模板专业化,无需重新实现一切

bob*_*gle 26 c++ templates class specialization

我有一个像这样的模板化课程:

template<typename T>
class A
{
    protected:
    std::vector<T> myVector;

    public:
    /*
    constructors + a bunch of member functions here
    */
}
Run Code Online (Sandbox Code Playgroud)

我想只添加一个仅适用于1种给定类型的T的成员函数.是否可以完全执行此操作而无需专门化类并重新实现所有其他已存在的方法?

谢谢

Pio*_*cki 18

最简单和最干净的解决方案是static_assert()在方法体中使用a ,拒绝除所选类型之外的其他类型(在下面的示例中,只接受整数):

#include <type_traits>  
#include <vector>

template <typename T>
class A
{
public:
    void onlyForInts(T t)
    {
        static_assert(std::is_same<T, int>::value, "Works only with ints!");
    }

protected:
    std::vector<T> myVector;
};

int main()
{
    A<int> i;
    i.onlyForInts(1); // works !

    A<float> f;
    //f.onlyForInts(3.14f); // does not compile !
}
Run Code Online (Sandbox Code Playgroud)

OK CASE DEMO NOK CASE DEMO

这利用了这样的事实:只有在实际使用了一个类模板时,编译器才会实例化类模板的成员函数(而不是在类模板本身实例化时).并且通过上述解决方案,当编译器尝试这样做时,由于执行a而失败static_assert.

C++标准参考:

§14.7.1隐式实例化 [temp.inst]

  1. 除非已明确实例化或明确专门化了函数模板特化,否则在需要存在函数定义的上下文中引用特化时,将隐式实例化函数模板特化.除非调用函数模板显式特化或显式专用类模板的成员函数,否则在需要的上下文中调用函数时,将隐式实例化函数模板的默认参数或类模板的成员函数.默认参数的值.

  2. [ 例如:

    template<class T> struct Z {
      void f();
      void g();
    };
    
    void h() {
      Z<int> a;     // instantiation of class Z<int> required
      Z<char>* p;   // instantiation of class Z<char> not required
      Z<double>* q; // instantiation of class Z<double> not required
      a.f();        // instantiation of Z<int>::f() required
      p->g();       // instantiation of class Z<char> required, and
                    // instantiation of Z<char>::g() required
    }
    
    Run Code Online (Sandbox Code Playgroud)

    此示例中的任何内容都不需要class Z<double>,Z<int>::g()Z<char>::f()隐式实例化.- 结束例子 ]


Fyt*_*tch 12

是的,在C++ 03中可以使用CRTP(奇怪的重复模板模式):

#include <numeric>
#include <vector>

template<typename Derived, typename T>
struct Base
{
};

template<typename Derived>
struct Base<Derived, int>
{
    int Sum() const
    {
        return std::accumulate(static_cast<Derived const*>(this)->myVector.begin(), static_cast<Derived const*>(this)->myVector.end(), int());
    }
};

template<typename T>
class A : public Base<A<T>, T>
{
    friend class Base<A<T>, T>;

protected:
    std::vector<T> myVector;

public:
    /*
    constructors + a bunch of member functions here
    */
};

int main()
{
    A<int> Foo;
    Foo.Sum();
}
Run Code Online (Sandbox Code Playgroud)

  • 如果人们能解释他们为什么会贬低,我会很感激,所以我可以从错误中吸取教训. (17认同)
  • 非常感谢,这太棒了,不知道它! (2认同)

Kri*_*izz 6

作为一种替代解决方案,它也适用于普通的C++ 03(与static_assert或者enable_if解决方案相反),您可以添加额外的默认模板参数,这将使您拥有专用和非专用版本的类.然后,您可以从非专用版本继承您的专用版本.

这是一个示例代码段:

#include <vector>

template<typename T, bool unspecialized = false>
class A
{
  protected:
    std::vector<T> myVector;

  public:
    void setVec(const std::vector<T>& vec) { myVector = vec; }
    /*
    constructors + a bunch of member functions here
    */
};

template<>
class A<int, false> : public A<int, true>
{
  public: 
   int onlyForInt() {
      return 25;
   }
};

int main() {
  // your code goes here
  std::vector<int> vec;
  A<int> a;
  a.setVec(vec);
  a.onlyForInt();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果类具有非平凡的构造函数,则此解决方案的缺点是需要添加构造函数转发器.

  • 您必须在C++ 03中实现两次所有构造函数(因为您不能继承构造函数),并且由于辅助模板参数,您还必须为运算符编写大量样板代码. (2认同)