Предоставление метода только в частичной специализации - PullRequest
1 голос
/ 17 января 2020

У меня есть класс Matrix, и я хочу специальный метод len(), если R = 3 и C = 1. Я хотел бы иметь решение со стандартной библиотекой. Наследование не является решением, потому что оно не будет работать и в базовом классе.

Обычная специализация шаблона, такая как

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

, вынудит меня реализовать все другие Matrix<T, R, C> методы и прочее. (?)

Идеи:

  • 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");
}

Ответы [ 2 ]

4 голосов
/ 18 января 2020

Простое решение с использованием 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;
}
1 голос
/ 18 января 2020

Один из возможных подходов:

Объяснение в виде комментариев:

#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;
}
...