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

时间:2014-08-25 12:32:27

标签: c++ class templates specialization

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

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

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

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

由于

6 个答案:

答案 0 :(得分:17)

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

#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 !
}

OK CASE DEMO NOK CASE DEMO

这利用了这样一个事实,即编译器仅在实际使用了一个类模板的成员函数时才会实例化(不是在自己实例化类模板时)。通过上述解决方案,当编译器尝试这样做时,由于执行static_assert而失败。

C ++标准参考:

  

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

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

  2.   
  3. [示例:

    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
    }
    
         

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

  4.   

答案 1 :(得分:12)

是的,可以在C ++ 03中使用CRTP(Curiously recurring template pattern):

#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();
}

答案 2 :(得分:6)

作为替代解决方案,它也适用于普通的C ++ 03(与static_assertenable_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;
}

此解决方案的缺点是需要添加构造函数转发器(如果是class) 有非平凡的构造函数。

答案 3 :(得分:2)

@PiotrS的static_assert技术。很好地工作。但是,如果不知道代码重复,您可以专门研究单个成员函数,这也很好。只需为通用onlyForInts()提供一个空的无操作实现,并将其专门化为int

的类外
#include <vector>

template <typename T>
class A
{
public:
    void onlyForInts(T t)
    {
        // no-op
    }

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

template<>
void A<int>::onlyForInts(int t)
{
    // works  
}

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

    A<float> f;
    f.onlyForInts(3.14f); // compiles, but does nothing !
}

Live Example

如果您希望在不完全禁用通用行为的情况下拥有int特定行为,此技术会派上用场。

答案 4 :(得分:2)

答案中尚未给出的一种方法是使用标准库std::enable_if对继承到定义适当成员函数的主类的基类执行SFINAE

示例代码:

template<typename T, class Enable = void>
class A_base;

template<typename T>
class A_base<T, typename std::enable_if<std::is_integral<T>::value>::type>{
    public:
        void only_for_ints(){/* integer-based function */}
};

template<typename T>
class A_base<T, typename std::enable_if<!std::is_integral<T>::value>::type>{
    public:
        // maybe specialize for non-int
};

template<typename T>
class A: public A_base<T>{
    protected:
        std::vector<T> my_vector;
};

这种方法比空函数更好,因为你对API更严格,而且比static_cast更好,因为它根本不会进入函数内部(它不会存在) )并在编译时给你一个很好的错误信息(GCC显示“我的机器上没有名为'only_for_ints'的成员)。

这种方法的缺点是编译时间和代码膨胀,但我不认为它太大了。

(难道你不敢说C ++ 11的要求是一个缺点,我们是2014年的上帝,并且下一个标准已经完成了!)

另外,我注意到,您可能必须在基类中定义my_vector而不是final,因为您可能希望在成员函数中处理该数据。

在不复制大量代码的情况下,这样做的一个好方法是创建一个基类(好上帝)并在基类中继承该类。

示例:

template<typename T>
class base_data{
    protected:
        std::vector<T> my_vector;
};

template<typename T>
class A_base<T, typename std::enable_if<std::is_integral<T>::value>::type>: public base_bata<T>{
    public:
        void only_for_ints(){/* phew, finally. fiddle around with my_vector! */}
};

// non-integer A-base

template<typename T>
class A: public A_base<T>{
    protected:
        // helper functions not available in base
};

这确实留下了一个看起来很糟糕的多继承方案,但它非常可行,并且可以很容易地根据模板参数定义成员(以便将来进行校对)。

人们通常不喜欢多重继承或SFINAE看起来多么复杂/凌乱,但是现在我知道它不能没有它:静态代码的速度与动态代码的多态性!

答案 5 :(得分:0)

不确定我在哪里找到的,但是您可以在类内部使用= delete;作为函数定义,从而删除一般情况下的函数,然后在类外部明确地专门化:

template <typename T>
struct A
{
  auto int_only(T) -> void = delete;
};

template <> auto A<int>::int_only(int) -> void {}

int main()
{
  auto a_int = A<int>{};
  auto a_dbl = A<double>{};
  a_int.int_only(0);
  // a_dbl.int_only(3.14);  error: call to deleted member function
}

https://en.cppreference.com/w/cpp/language/function#Deleted_functions