【问题标题】:Partial specialization and SFINAE部分专业化和 SFINAE
【发布时间】:2017-02-13 20:11:29
【问题描述】:

假设我有以下 Matrix 模板类,并且需要将向量表示为 1 x RowSize 或 ColSize x 1 矩阵(这样我就可以重用许多与向量兼容的矩阵运算符:乘以 2 个矩阵,乘以矩阵通过标量等):

template <class T, size_t ColumnSize, size_t RowSize>
struct Matrix {
    T [ColumnSize][RowSize];
}

我有两个问题:

1) 如果我没记错的话,我可以通过部分专业化或在矩阵方法上使用 SFINAE 来实现这一点(例如,当 ColSize 或 RowSize 为 1 时启用“长度”方法)。上述选项的优缺点是什么?

2) 如果我选择使用部分特化,有没有办法为行向量和列向量定义一个特化,而不是这样:

template <class T, size_t ColumnSize>
struct Matrix<T, ColumnSize, 1> {
    T length() const;

    T [ColumnSize][RowSize];
}

template <class T, size_t RowSize>
struct Matrix<T, 1, RowSize> {
    T length() const;

    T [ColumnSize][RowSize];
}

【问题讨论】:

  • 您应该提供更多上下文,否则您的问题是“基于意见的”。
  • 如果你问两者的优缺点,我认为这会更好,因为这样更容易客观地回答。
  • 建议:您可以将参数定义为template&lt;class T, size_t Size, VectorOrientation orientation&gt; class Matrixenum class VectorOrientation { ColumnVector, RowVector };。那么我认为就不会有歧义了。
  • 请注意,在 C++ 中,二维数组主要是行,您应该使用 T [RowSize][ColumnSize]; 或更好的包装类在 std::array&lt;T,RowSize*ColumnSize&gt; 上。
  • 您仍然可以拥有template&lt;class T, size_t ColumnSize, size_t RowSize&gt; using MatrixAlias = Matrix&lt;T, ColumnSize * RowSize, ColumnSize == 1 ? ColumnVector : RowSize == 1 ? RowVector : (throw "one of row or column size must be 1")&gt;;

标签: c++


【解决方案1】:

这真的取决于要求是“通用矩阵不能有长度方法”(那么应该使用SFINAE或继承),还是“不能在通用矩阵上调用length”(然后是static_assert length 正文的内部适用)。第三种选择是不做任何事情并使length 适用于通用矩阵,但是还有其他仅适用于向量的操作。

对于“一般的矩阵不能有长度方法”。为了节省空间,我将使用int 和更短的符号名称。而不是int_,您应该使用std::integral_constantint_ 包装器是必需的,因为如果参数是非类型参数,语言限制禁止专门处理更复杂的计算。因此,我们将参数设为类型,并将值包装到其中。下面不使用SFINAE,而是继承。有了矢量混合基类的d(),您可以随时从混合类内部访问矢量的数据。

template<int> struct int_;

template<typename D, typename S>
struct V { };

template<typename T, int A, int B>
struct M : V<M<T, A, B>, int_<A * B>> {
   T data[A][B];
};

template<typename T, int A, int B>
struct V<M<T, A, B>, int_<A + B - 1>> { 
   int length() const { return A * B; }

   M<T, A, B> *d() { return static_cast<M<T, A, B>*>(this); }
   const M<T, A, B> *d() const { return static_cast<const M<T, A, B>*>(this); }
};

现在是

int main() { 
   M<float, 1, 3> m1; m1.length();
   M<float, 3, 1> m2; m2.length();
   // M<float, 3, 2> m3; m3.length(); error
}

对于“length不能在通用矩阵上调用”,可以使用“static_assert”

template<typename T, int A, int B>
struct M {
   int length() const {
      static_assert(A == 1 || B == 1, "must not be called on a matrix!");
      return A * B;
   }

   T data[A][B];
};

选择最合适的

【讨论】:

    【解决方案2】:

    SFINAE 只能根据自己的参数禁用模板声明。使用封闭类的参数禁用非模板成员函数(例如length)有点不自然。该技术如下所示:

    template <class T, size_t RowSize, size_t ColumnSize>
    struct Matrix {
        // SFINAE turns a non-template into a template.
        // Introduce a fake dependency so enable_if resolves upon function call.
        template< typename size_t_ = size_t >
        static constexpr
        // Now write the actual condition within the return type.
        std::enable_if_t< RowSize == 1 || ColumnSize == 1
        , size_t_ > length() const;
            { return RowSize * ColumnSize; }
    
        T [ColumnSize][RowSize];
    }
    

    如果你能忍受这种丑陋,那么你就会得到你想要的:所需类型的函数,当条件不满足时它会完全消失。不需要其他支持。

    另一方面,部分特化会影响整个类定义。由于在每个部分特化中复制整个类通常是糟糕的设计,因此按照 Johannes 的描述使用继承。

    只是为他的答案添加一个替代方案,SFINAE 可以在部分专业化中使用,以避免聪明的代数和int_ 问题。

    // Add "typename = void" for idiomatic class SFINAE.
    template<size_t RowSize, size_t ColumnSize, typename = void>
    struct maybe_vector_interface { }; // Trivial specialization for non-vectors
    
    // Partial specialization for vectors:
    template<size_t RowSize, size_t ColumnSize>
    struct maybe_vector_interface< RowSize, ColumnSize,
        std::enable_if_t< RowSize == 1 || ColumnSize == 1 > > { 
        static constexpr int length() const
            { return RowSize * ColumnSize; }
    };
    
    template<typename T, size_t RowSize, size_t ColumnSize>
    struct Matrix
        : maybe_vector_interface<RowSize, ColumnSize> {
        T data[RowSize][ColumnSize];
    };
    

    【讨论】:

      猜你喜欢
      • 2015-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多