【问题标题】:Pass by reference if possible, by value otherwise如果可能,通过引用传递,否则通过值传递
【发布时间】:2019-08-22 00:37:09
【问题描述】:

我想使用模板函数中另一个矩阵的转换来创建我的类 Matrix 的实例。

Matrix<T> m(A.tri_lo());

转换,这里tri_lo()返回一个新值,所以这里我的代码抛出一个错误:

error C2662: 'Matrix<long double> Matrix<long double>::tri_lo(bool)' : cannot convert a 'this' pointer from 'const Matrix<long double>' to 'Matrix<long double> &'

我尝试重载按值传递的构造函数,但我无法让它工作。这是我的构造函数:

Matrix() : data{ {T{}} } {}; // Implemented
Matrix(std::vector<std::vector<T>> _data) : data{ _data } {}; // Implemented
Matrix(unsigned int const lines, unsigned int const cols) { // Implemented
    for (unsigned int i = 0; i < lines; i++) { this->data.push_back(std::vector<T>(cols, T())); }
};
template<class T2> Matrix(Matrix<T2> const& other) : data{ other.data } {}; // Implemented
template<class T2> Matrix(Matrix<T2> const other) : data{ other.data } {} // Implemented

我哪里出错了?

编辑:这是上下文。

template<class T>
template<class T2>
auto Matrix<T>::operator-(Matrix<T2> const& other) {
    assert(this->lines() == other.lines());
    assert(this->cols() == other.cols());

    decltype(std::declval<T>() - std::declval<T2>()) T3;

    Matrix<T3> res(this->lines(), this->cols());

    for (unsigned int const i = 0; i < this->lines(); i++) {
        for (unsigned int const j = 0; j < this->cols(); i++) {
            res[i][j] -= other[i][j];
        }
    }

    return res;
}

这里是full pastebin。如果需要,请随意包含一个小的代码审查!

【问题讨论】:

  • 成员函数tri_lo是否标记为const
  • 你还没有给我们enough code to reproduce the problem,但我有可能A是一个const变量而tri_lo是一个非常量函数?
  • 是的,A 在 const 变量中。 tri_lo 声明如下:Matrix&lt;T&gt; tri_lo(bool include_diag = false); 在类声明中。
  • @Magix 将 const 添加到其声明中:Matrix&lt;T&gt; tri_lo(bool include_diag = false) const; 或从 A 的声明中删除 const
  • 我试过了,但我相信它并没有解决问题,尽管我可能需要在任何地方添加 const 才能真正检查。为什么添加 const 会起作用?

标签: c++ constructor parameter-passing c++14 pass-by-reference


【解决方案1】:

主要问题

您的代码存在很多 Visual Studio 没有发现的问题,但这仍然会破坏代码。

例如,在您的 pastebin 文件的第 86 和 87 行:

decltype (std::declval<T>()*std::declval<T2>()) T3;
Matrix<T3> result = Matrix<T3>::gen_full(this->lines(), other.cols());

您声明了一个名为T3 的变量,然后尝试将其用作Matrix 的模板参数。应该是这样的:

// Declare T3 as a type
using T3 = decltype (std::declval<T>()*std::declval<T2>());
// Now we can use T3
Matrix<T3> result = Matrix<T3>::gen_full(this->lines(), other.cols());

或者在这里,gen_full

template<class T>
Matrix<T> Matrix<T>::gen_full(unsigned int lines, unsigned int cols, T value){
    for(unsigned int i = 0; i < lines; i++) {
        std::vector<T> line;
        for(unsigned int j = 0; j < cols; j++) {
            line.push_back(value);
        }
        this->data.push_back(line); // Error here
    }
};

您正在使用this,但gen_full 是一个静态函数,因此this 不可用。

我们可以改写为:

template<class T>
Matrix<T> Matrix<T>::gen_full(unsigned int lines, unsigned int cols, T value){
    Matrix<T> m; 
    for(unsigned int i = 0; i < lines; i++) {
        std::vector<T> line;
        for(unsigned int j = 0; j < cols; j++) {
            line.push_back(value);
        }
        m.data.push_back(line); // Error here
    }
    return m; 
};

第 346 行和第 348 行的问题与第 86 行和第 87 行的问题相同:

decltype(std::declval<T>() - std::declval<T2>()) T3;

Matrix<T3> res(this->lines(), this->cols());

我们可以像之前那样修复它(使用using T3 = decltype(...)

在第 350 行,您将 i 声明为 const,然后将其递增。我们可以删除const 并且它可以工作。

其他问题

一旦我们解决了主要问题,还有一些其他问题我们只能通过尝试实例化类来解决。

例如,我们可以使用一个虚拟函数让编译器为我们检查:

void foo() {
    // Forces the compiler to instantiate Matrix<double>
    Matrix<double> A;
    Matrix<double> B(A.tri_lo()); 
}

当我们尝试这样做时,我们会遇到一些神秘的错误,例如第 260 行:

Matrix<T> res(this->lines(), this->cols());

Gcc 给了我错误

<source>: In instantiation of 'Matrix<T> Matrix<T>::tri_lo(bool) const [with T = double]':
<source>:365:31:   required from here
<source>:262:15: error: passing 'const Matrix<double>' as 'this' argument discards qualifiers [-fpermissive]
  262 |     Matrix<T> res(this->lines(), this->cols());
      |               ^~~

这意味着您正在尝试在 const 上下文中使用 不是 const 的函数(例如 lines()cols())(因为 tri_lo 是 const)

我们可以通过将 lines()cols() 标记为 const 来解决此问题:

// On line 32 and 33
unsigned int cols() const; // Implemented
unsigned int lines() const; // Implemented

这里也是:

// Lines 71 to 75
template<class T>
unsigned int Matrix<T>::cols() const { return this->data.size(); };

template<class T>
unsigned int Matrix<T>::lines() const { return this->data[0].size(); };

是什么导致了最初的问题?

据我所知,最初的问题是因为 lines()cols() 没有被标记为 const。

结论

Visual Studio 没有发现很多错误。使用单独的编译器是个好主意,比如gccclang,这样可以更快更快地捕获错误。您可以通过https://godbolt.org 在线使用它们,也可以在本地安装它们。

这是您的代码的原始版本,以及 gcc 显示的错误:https://godbolt.org/z/5eiRNw

这是您代码的更新版本,已修复错误(包括您原始帖子中描述的错误):https://godbolt.org/z/vFlyvk

您仍然需要添加Matrix&lt;T&gt;::gen_uninitialized 的实现,并且在第 226 行,clang 警告您 std::vector&lt;T&gt; diag(); 被解释为名为 diag 的函数的前向声明(删除括号),但其他所有内容看起来不错!

【讨论】:

  • 我实现了按值传递构造函数来尝试解决上述错误。它在它还不存在的时候就出现了。
  • 您能否发布足够多的代码,以便我可以尝试编译它以查看您遇到的相同错误?这真的帮我解决它:)
  • 编辑了我的问题
  • 非常感谢您的出色回答和代码审查。这对我很有帮助!
  • 很高兴我能帮上忙!以后你应该使用godbolt.org;它对于在不同的编译器上测试代码非常有用。它帮助我发现了很多错误,它还会向你展示为函数生成的程序集
猜你喜欢
  • 1970-01-01
  • 2020-04-01
  • 2013-04-21
  • 1970-01-01
  • 2012-04-14
  • 2015-03-12
  • 1970-01-01
  • 2015-02-24
  • 1970-01-01
相关资源
最近更新 更多