【问题标题】:Providing a method only in a partial specialization仅在部分专业化中提供方法
【发布时间】:2020-01-17 14:50:19
【问题描述】:

我有Matrix 的课程,如果R = 3C = 1,我想要len() 的专用方法。 我想有标准库的解决方案。 继承不是解决方案,因为它也不适用于基类。

普通模板特化,如

template<class T>
class Matrix<T, 3, 1> {
public:
    T len() const {return 3;}
};

会迫使我实现所有其他Matrix&lt;T, R, C&gt; 方法和东西。 (?)

想法:

  • std::enable_if
  • constexpr
  • ...
#include <vector>

template<class T, std::size_t R, std::size_t C>
class Matrix {
private:
    std::vector <T> data;
public:
    T len() const;
};

一个肮脏的解决方案是:

template<class T, std::size_t R, std::size_t C>
T Matrix<T, R, C>::len() const {
    if constexpr (R == 3 && C == 1) {
        return 3;
    }
    throw std::runtime_error("len not available");
}

【问题讨论】:

  • @Evg:他希望将一行矩阵视为向量,因此具有计算长度等向量功能。
  • “普通模板专业化 [..] 将迫使我实现所有其他 Matrix&lt;T, R, C&gt; 方法和东西。” 如果您拆分基本 Matrix 实现和可定制部分,你应该避免重复。
  • C++20 会有requires
  • static_assert(R ==3 &amp;&amp; C == 1); 似乎比 throw 好。
  • @Evg:浮点向量的长度应该是浮点数。此外,为什么需要将其限制为 3 列向量并不是特别相关;问题是怎么做。

标签: c++ templates c++17 template-specialization


【解决方案1】:

使用std::enable_if 的简单解决方案。

#include <type_traits> // std::enable_if
#include <vector>      // std::vector

template <class T, std::size_t R, std::size_t C>
class Matrix {
 private:
  std::vector<T> data;

 public:
  template <std::size_t C_ = C, typename = std::enable_if_t<C_ == 1>>
  std::size_t len() const {
    double res = 0;
    for (const auto& x : data) {
      res += std::pow(x, 2);
    }
    return std::sqrt(res);
  }

};

替代的外联定义:

template <class T, std::size_t R, std::size_t C>
class Matrix {
 private:
  std::vector<T> data;

 public:
  template <std::size_t R_ = R,
            std::size_t C_ = C,
            typename E1 = std::enable_if_t<R_ == 3>,
            typename E2 = std::enable_if_t<C_ == 1>>
  double len() const;
};

template <class T, std::size_t R, std::size_t C>
template <std::size_t R_, std::size_t C_, typename E1, typename E2>
double Matrix<T, R, C>::len() const {
  double res = 0;
  for (const auto& x : data) {
    res += std::pow(x, 2);
  }
  return std::sqrt(res);
}

例子:

#include <iostream>  // std::cout, std::endl

int main(int argc, char* argv[]) {

  Matrix<double, 3, 1> vec;
  std::cout << "vec length: " << vec.len() << std::endl;
  // output: vec length: 3

  // Matrix<double, 3, 3> mat;
  // std::cout << "mat length: " << mat.len() << std::endl; // does not compile

  return 0;
}

【讨论】:

  • 如何声明越界? template&lt;class T, std::size_t R, std::size_t C&gt; template&lt;std::size_t R_ = R, typename = std::enable_if_t&lt;R_ == 3&gt;, std::size_t C_ = C, typename = std::enable_if_t&lt;C_ == 1&gt;&gt; T Matrix&lt;T, R, C&gt;::len() const { return std::sqrt(std::pow(this-&gt;v(0), 2) + std::pow(this-&gt;v(1), 2) + std::pow(this-&gt;v(2), 2)); } 似乎不可能。
【解决方案2】:

一种可能的方法:

内联解释为 cmets:

#include <iostream>

template<class T, int R, int C>
struct Matrix;

namespace MatrixOperations
{
    // default handling is to prevent compilation
    template<class Mat>
    auto len(Mat const&) -> void = delete;

    // special case for a 3,1 matrix
    template<class T>
    auto len(Matrix<T, 3, 1> const&) -> T
    {
        return T(3);
    }
};

template<class T, int R, int C>
struct Matrix
{
    // ForceDeduce is a defaulted type.
    // The argument of this type is defaulted
    // It's purpose is to ensure that this function is not actually compiled until it is used
    // This will prevent the call to MatrixOperations::len producing an error unless this function
    // is called
    template<class ForceDeduce = int*>
    T len(ForceDeduce = nullptr) const
    {
        return MatrixOperations::len(*this);
    }
};

int main()
{
    Matrix <int, 3, 1> m31;
    std::cout << m31.len() << std::endl;

    Matrix <int, 3, 2> m32;
// fails to compile
//    std::cout << m32.len() << std::endl;
}

【讨论】:

  • 一旦您费尽心思将Matrix::len 设为模板,为什么不让它对SFINAE 友好(并且不使用命名空间版本)?
猜你喜欢
  • 2021-07-26
  • 2015-02-18
  • 2021-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多