具有不同返回类型的模板类成员

时间:2021-07-11 12:35:07

标签: c++ c++20

我有一个模板矩阵类,例如(简化形式):

template<typename Scalar, typename Accessor = GenericAccessor>
class Matrix {
public:
  Matrix(size_t num_rows, size_t num_cols, const std::vector<Scalar>& elems)
: num_rows_(num_rows), num_cols_(num_cols),
  accessor_(num_rows, num_cols),
  storage_(elems) {}

private:
  size_t num_rows_;
  size_t num_cols_;

  Accessor accessor_;

  std::vector<Scalar> storage_;
}

这个类有 Transpose 方法,它将返回一个新的矩阵,其中行和列交换了和不同的访问器类型。 我当前的实现似乎不起作用,因为我尝试返回不同专业化的矩阵:

  Matrix Transpose() {
    if (dynamic_cast<TransposeAccessor*>(accessor_)) {
      return Matrix<Scalar, GenericAccessor>(num_cols_, num_rows_, storage_);
    }

    return Matrix<Scalar, TransposeAccessor>(num_cols_, num_rows_, storage_);
  }

我知道返回类型应该匹配类特化,但是我该如何正确实现 Transpose 方法呢?

3 个答案:

答案 0 :(得分:2)

'Matrix' 在这里指的是当前上下文,即您声明了返回一种类型的函数,而您试图返回两种不同的类型。我假设它们没有关系。您必须静态地执行选择,即具有两个单独的“转置”专业化。如果访问器可以动态改变,那么你必须做一些类型擦除。

这是出于“疯狂的想法”,可能不应该遵循。

#include <iostream>
#include <concepts>

struct GenericAccesstor {};
struct TransposeAccesstor {};

template<typename T>
concept GenericAccess = requires(T a) {
        {a} -> std::convertible_to<GenericAccesstor>;
};

template<typename T>
concept TransposedAcccess = requires(T a) {
        {a} -> std::convertible_to<TransposeAccesstor>;
};

template < typename Scalar, typename Accessor = GenericAccesstor >
struct Matrix {
    using access_type =  Accessor;
    
    template < GenericAccess A = Accessor >
    auto  Transpose()  -> Matrix<Scalar, TransposeAccesstor > 
    {  return  Matrix<Scalar, TransposeAccesstor>(); }
    
    template < TransposedAcccess A = Accessor >
    auto  Transpose()  -> Matrix<Scalar, GenericAccesstor > 
    {  return  Matrix<Scalar, GenericAccesstor>(); } 
    
};

我们可以检查它是如何工作的:

int main()
{
    Matrix<int, GenericAccesstor> m;
    
    auto m2 = m.Transpose();  
    auto m3 = m2.Transpose();
        
    std::cout << typeid(decltype(m)::access_type).name() << std::endl;
    std::cout << typeid(decltype(m2)::access_type).name() << std::endl;
    std::cout << typeid(decltype(m3)::access_type).name() << std::endl;
}

/** Output:
16GenericAccesstor
18TransposeAccesstor
16GenericAccesstor  */

Tbh,我根本不会以这种方式构建 Matrix,因为听起来转置矩阵是一种与原始矩阵不同的类型,所以我不确定完全实现的好坏。

答案 1 :(得分:2)

如果我正确理解您的问题,您想返回 Matrix<Scalar, TransposeAccessor > if Accessor = GenericAcessor 反之亦然。

一种方法是创建一个辅助类模板,它允许您通过专门化模板来为您的访问器获取相反类型(您可能需要选择一个更好的名称):< /p>

template<typename T>
struct accessor_trait;

template<>
struct accessor_trait<GenericAcessor> {
    using oppsite_type = TransposeAccessor;
};

template<>
struct accessor_trait<TransposeAccessor> {
    using oppsite_type = GenericAcessor;
};

然后您的 Transpose 函数可能如下所示:

  auto Transpose() {
      return Matrix<Scalar, typename accessor_trait<Accessor>::oppsite_type>(num_cols_, num_rows_, storage_);
  }

如果您需要在不同的地方使用 oppsite 访问器,您可能希望这样编写:

template<typename Scalar, typename Accessor = GenericAcessor>
class Matrix {
public:
  // create a name alias here to be used at other places
  using OppsiteAccessor = Matrix<Scalar, typename acessor_trait<Accessor>::oppsite_type>;

  Matrix(std::size_t num_rows, std::size_t num_cols, const std::vector<Scalar>& elems)
: num_rows_(num_rows), num_cols_(num_cols),
  accessor_(num_rows, num_cols),
  storage_(elems) {}

  Matrix<Scalar, OppsiteAccessor> Transpose() {
      return Matrix<Scalar, OppsiteAccessor>(num_cols_, num_rows_, storage_);
  }

private:
  std::size_t num_rows_;
  std::size_t num_cols_;

  Accessor accessor_;

  std::vector<Scalar> storage_;
};

答案 2 :(得分:1)

auto Transpose() {
  if constexpr (std::is_same_v<TransposeAccessor,decltype(accessor_)>) {
    return Matrix<Scalar, GenericAccessor>(num_cols_, num_rows_, storage_);
  } else {
    return Matrix<Scalar, TransposeAccessor>(num_cols_, num_rows_, storage_);
  }
}