【问题标题】:Member specialization for a templated class for groups of types?类型组的模板类的成员特化?
【发布时间】:2016-10-10 11:33:08
【问题描述】:

我有一个矩阵类,我想针对不同的矩阵类型(int、float、double)以不同的方式将矩阵打印到终端。我想实现这个:

  • 如果矩阵类型为int,则使用printf("%d ",matrix[i][j])打印矩阵
  • 如果矩阵类型为floatdouble,则使用printf("%.3f ",matrix[i][j]) 打印矩阵
  • 否则,抛出错误

以下是我所拥有的相关部分:

...

template <class T>
class Matrix2D {
private:
    std::vector< std::vector<T> > matrix;
public:
    ...
    void print() const; // print the whole matrix
}

...

template <class T>
void Matrix2D<T>::print() const {
    // throw an error
}

template <>
void Matrix2D<int>::print() const {
    // print matrix using printf("%d ",matrix[i][j])
}

template <>
void Matrix2D<float,double>::print() const {
    // print matrix using printf("%.3f ",matrix[i][j])
}

但是使用Matrix2D&lt;float,double&gt; 会给我错误消息error: wrong number of template arguments (2, should be 1)。但是,我希望对 floatdouble 类型矩阵都有一个通用的 print() 函数(不想复制相同的东西两次)。实现这一目标的最简单方法是什么?谢谢!

【问题讨论】:

    标签: c++ class templates object matrix


    【解决方案1】:

    只是作为提议的模板化解决方案的替代方案,使用旧的良好函数重载:

    public:
        void print() const
        {
            for (auto const& row : matrix)
                for (auto const& v : row)
                    print(v);
        }
    
    private:
        static void print(int val)
        {
            printf("%d ", val);
        }
    
        static void print(float val)
        {
            printf("%.3f", val);
        }
    

    【讨论】:

    • 这就是我最终的做法。这是一个轻量级的解决方案,避免了额外的库。我可以将我的大部分显示函数写在一个片段中,然后调用这个重载的打印函数,从而最大限度地减少额外的代码编写。
    【解决方案2】:

    所问的问题希望Matrix2D&lt;short&gt;::print() 抛出错误。为此,您可以使用标签调度。

    标签 - 简单

    天真的方法要求您直接为每种类型专门化标签。

    namespace detail
    {
    
    struct int_tag{};
    struct float_tag{};
    struct error_tag{};
    
    template<typename T> struct choose_tag { using type = error_tag; };
    
    template<> struct choose_tag<int> { using type = int_tag; };
    template<> struct choose_tag<double> { using type = float_tag; };
    template<> struct choose_tag<float> { using type = float_tag; };
    
    template<typename T>
    using tag = typename choose_tag<T>::type;
    
    }
    

    标签 - 类型列表

    您可以使用 Boost.Hana(或其他 MPL 解决方案)来避免这种情况。首先,定义检查列表中的类型:

    template<typename T, typename... Us>
    constexpr bool contains =
            hana::any_of(hana::tuple_t<Us...>, hana::partial(hana::equal, hana::type_c<T>))();
    

    然后只需 enable_if 为您的标签键入:

    template<typename T, typename = std::void_t<>>
    struct choose_tag
    { using type = error_tag; };
    
    template<typename T>
    struct choose_tag<T, enable_if_t<contains<T, int>>>
    { using type = int_tag; };
    
    template<typename T>
    struct choose_tag<T, enable_if_t<contains<T, double, float>>>
    { using type = float_tag; };
    

    通用 - print 实现

    然后声明使用它们的函数模板:

    template<typename T>
    void print_matrix(detail::int_tag, T&&) {
        cout << __PRETTY_FUNCTION__ << endl;
    }
    
    template<typename T>
    void print_matrix(detail::float_tag, T&&) {
        cout << __PRETTY_FUNCTION__ << endl;
    }
    
    template<typename T>
    void print_matrix(detail::error_tag, T&&) {
        cout << __PRETTY_FUNCTION__ << endl;
    }
    

    然后调用:

    template <class T>
    class Matrix2D {
    private:
        std::vector< std::vector<T> > matrix;
    public:
    
        void print() const {
            print_matrix(detail::tag<T>{}, *this);
        }
    };
    

    这是simpletype list 版本的实时示例。

    【讨论】:

      【解决方案3】:

      您可以使用enable_iftype traits,执行以下操作:

      template<class T> class MyClass
      {
      public:
          // this one will be created if the argument is of a floating point type, i.e.
          // double or float
          template<typename U = T>
          typename std::enable_if<std::is_floating_point<U>::value, void>::type
          print(U v)
          {
              std::cout << "float" << std::endl;
          }
      
          // this one will be created if the argument is of an integral type, i.e.
          // bool, char, char16_t, char32_t, wchar_t, short, int, long, long long
          template<typename U = T>
          typename std::enable_if<std::is_integral<U>::value, void>::type
          print(U v)
          {
              std::cout << "integer" << std::endl;
          }
      
      };
      
      
      int main() {
          MyClass<int>c;
          c.print(1);
          c.print(1.f);
      }
      

      输出:

      integer
      float
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多