简化大量模板专业化

时间:2018-07-19 14:37:06

标签: c++ templates template-specialization boilerplate template-classes

因此,我对此模板有很多模板专长:

template <typename T> // Same
struct foo { // Same
    using type_name = T; // Same
    foo(const int base) : _base(base) {} // May take other parameters
    void func(const T& param) {} // This function signature will be the same but body will differ
    int _base; // Same but may have more members
}; // Same

因此,专门化的示例将是:

template<>
struct foo<float> {
    using type_name = T;
    foo(const int base, const int child) : _base(base), _child(child) {}
    void func(const T& param) { cout << param * _child << endl; }
    int _base;
    int _child;
};

很明显,这是一个玩具示例,_func的正文将涉及更多。但是我认为这表达了这个想法。很明显,我可以创建一个宏来帮助样板,并将功能的特殊版本的实现放入实现文件中。

但是我希望C ++为我提供一种无需宏即可执行此操作的方法。我还有另一种方法可以避免一遍又一遍地写样板吗?

4 个答案:

答案 0 :(得分:3)

您可以对该函数进行多种专门化处理,但不能针对整个类进行

像这样

#include <iostream>
#include <string>

template<typename T>
struct foo {
    //common generic code
    using type_name = T;
    foo(const int base, const int child) : _base(base), _child(child) {}
    void func(const T& param);
    int _base;
    int _child;
};

template<>
void foo<float>::func(const type_name&) {
    //implementation
    std::cout << "float" << std::endl;
}

template<>
void foo<int>::func(const type_name&) {
    //implementation
    std::cout << "int" << std::endl;
}


int main() {
    foo<int> tint(0, 0);
    foo<float> fint(0, 0);

    tint.func(0);
    fint.func(0);
}

答案 1 :(得分:1)

您可以使用数据结构的轻继承性来帮助您将成员布局和构造函数定义中的差异与主模板分开。

//Define an internal aggregate type you can specialize for your various template parameters
template <typename T>
struct foo_data {
    foo(const int base) : _base(base) {}
    int _base;
};

//Then derive privately from the data struct (or publicly if you really desire)
template <typename T>
struct foo : private foo_data<T> {
    using type_name = T;
    using foo_data<T>::foo_data<T>; //Make the base class constructors visible
    void func(const T& param); //Use member specialization as suggested by the other answer
};

我将让您来决定这种方法是否更好,但是结果是所有常见部分与所有罕见部分都完全分开了。

在另一个答案下的评论中,我错误地将其描述为CRTP。它不是,也没有CRTP的任何缺点。

如果您确实需要保留标准布局,则可以使用显式委派和完美的转发来手动模拟继承。

template <typename T>
struct foo {
    using type_name = T;
    template <typename... Args>
    foo(Args&&... args) : base_data_(std::forward<Args>(args)...) {}
    void func(const T& param); //Use member specialization as suggested by the other answer
    foo_data<T> base_data_; 
};

一个缺点是,我认为委托的构造函数不能正确编写SFINAE,它还会吃noexcept指定符和explicit。解决这些问题(如果需要)留给读者练习。

答案 2 :(得分:1)

在实现模板化类型的专业化时,没有很好的方法来避免符号上的某些冗余。有一些技术可以避免实际代码的重复,例如

  1. 使用特征模板来提供特定于类型的内容

    template<typename T>
    struct foo_traits { ... };  // provide many specialisations
    
    template<typename T>        // no specialisations
    struct foo
    {
        using traits = foo_traits<T>;
    
        template<typename...Aars>
        explicit foo(Args&&...args)
         : data(std::forward<Args>(args)...) {}
    
        int do_something_specific(T x)
        { return traits::do_something(data,x); }
      private:
        typename traits::data data;
    };
    
  2. 一种非常相似的方法是使用专门的基类:

    template<typename T>
    struct foo_base { ... };    // provide many specialisations
    
    template<typename T>        // no specialisations
    struct foo : foo_base<T>
    {
        using base = foo_base<T>;
    
        template<typename...Aars>
        explicit foo(int m, Args&&...args)
         : base(std::forward<Args>(args)...)
         , more_data(m) {}
    
        int do_something_specific(T x)
        { return base::do_something(x,more_data); }
      private:
        int more_data;
    };
    

    foo的构造函数是可变参数模板,以允许基类的构造函数接受任意数量和类型的参数。

  3. 其中的一个可以使用公共基类并专门化派生类。可以使用Curiously recurring template pattern (CRTP)

    template<typename Derived>
    struct foo_base            // no specializations
    {
        using type = typename Derived::type;
        int do_something(type x)
        {
            auto result = static_cast<Derived*>(this)->specific_method(x);
            return do_some_common_stuff(result);
        }
      protected:
        foo_base(type x) : data(x) {}
        type data;
      private:
        int do_some_common_stuff(type x)
        { /* ... */ }
    };
    
    template<typename T>       // some specialisations
    struct foo : foo_base<foo<T>>
    {
        using base = foo_base<foo>;
        using type = T;
        using common_type = typename base::common_type;
        using base::do_something;
        explicit foo(type x, type y)
          : base(x), extra_data(y) {}
      protected:
        type specific_method(type x)
        { /* ... */ }
      private:
        type extra_data;
    };
    

    请注意,foo_base已经是一个模板(不同于普通多态的情况),因此您已经可以在那里进行很多特定的工作。只有做得不同的事情(不仅是不同类型的事情)才需要foo的专业化。

  4. 最后,您可以将这些方法结合起来,例如将traits类与CRTP结合起来。

所有这些方法都实现了某种类型的 static compile-time 多态性,而不是真实的或 dynamic 多态性:没有虚函数因此没有虚拟表,也没有查找表的开销。全部在编译时解决。

答案 3 :(得分:0)

这通常是通过继承完成的-将不可变部分放入基类,并专门化子级。

我认为您不需要为此举一个例子,但是如果您需要,请告诉我。