【问题标题】:Troubles understanding const in c++ (cannot bind non-const lvalue reference)难以理解 C++ 中的 const(无法绑定非 const 左值引用)
【发布时间】:2021-06-12 01:40:33
【问题描述】:

我正在尝试用 C++ 构建一个张量类。这仅适用于个人项目以在 C++ 中进行一些练习,并且它有点工作,但现在我遇到了一些我不太理解的 C++ 问题。这是我的张量类的主要结构,省略了一些不相关的函数。

#include <vector>

template<typename T> struct Tensor {
    // Support for up to 5 dimensions
    T& at(std::vector<std::size_t> indices); // Calls the other at() functions depending on the vector size
    T& at(std::size_t d1);
    T& at(std::size_t d1, std::size_t d2);
    T& at(std::size_t d1, std::size_t d2, std::size_t d3);
    T& at(std::size_t d1, std::size_t d2, std::size_t d3, std::size_t d4);
    T& at(std::size_t d1, std::size_t d2, std::size_t d3, std::size_t d4, std::size_t d5);
    Tensor transpose();
    Tensor matmul(Tensor& rhs) {
        rhs.at(0);
        return Tensor();
    }
};

在测试 matmul() 以将两个张量相乘时,以下代码有效:

void works() {
    Tensor<float> tensor1;
    Tensor<float> tensor2 = tensor1.transpose();
    Tensor<float> tensor3 = tensor1.matmul(tensor2);
}

但是,以下代码我没有明确创建 tensor1 作为 tensor1 的转置失败:

void fails() {
    Tensor<float> tensor1;
    Tensor<float> tensor = tensor1.matmul(tensor1.transpose());
}

抛出的第一个错误是

tensor.test.cpp:26:60: error: cannot bind non-const lvalue reference of type 'Tensor<float>&' to an rvalue of type 'Tensor<float>'
   26 |     Tensor<float> tensor = tensor1.matmul(tensor1.transpose());
      |                                           ~~~~~~~~~~~~~~~~~^~

根据我的谷歌搜索和对 C++ 的有限理解,我尝试将 matmul() 的定义更改为

Tensor matmul(const Tensor& rhs) { // <-- const added

但是,如果我这样做,我会得到一个不同的错误:

tensor.hpp: In instantiation of 'Tensor<T> Tensor<T>::matmul(const Tensor<T>&) [with T = float]':
tensor.test.cpp:21:43:   required from here
tensor.hpp:13:15: error: passing 'const Tensor<float>' as 'this' argument discards qualifiers [-fpermissive]
   13 |         rhs.at(0);
      |         ~~~~~~^~~

编辑 1: 在 cmets 之后,使用 Tensor matmul(const Tensor&amp; rhs); 添加所有 const 定义的所有 at() 函数解决了我的问题。但是现在代码看起来像这样:

T& at(std::vector<std::size_t> indices);
T& at(std::size_t d1);
T& at(std::size_t d1, std::size_t d2);
T& at(std::size_t d1, std::size_t d2, std::size_t d3);
T& at(std::size_t d1, std::size_t d2, std::size_t d3, std::size_t d4);
T& at(std::size_t d1, std::size_t d2, std::size_t d3, std::size_t d4, std::size_t d5);
        
const T& at(std::vector<std::size_t> indices) const;
const T& at(std::size_t d1) const;
const T& at(std::size_t d1, std::size_t d2) const;
const T& at(std::size_t d1, std::size_t d2, std::size_t d3) const;
const T& at(std::size_t d1, std::size_t d2, std::size_t d3, std::size_t d4) const;
const T& at(std::size_t d1, std::size_t d2, std::size_t d3, std::size_t d4, std::size_t d5) const;

这意味着相当多的重复代码。我想知道这是否可以改进。

【问题讨论】:

    标签: c++ c++17 pass-by-reference


    【解决方案1】:

    根据我的谷歌搜索和对 C++ 的有限理解,我尝试将 matmul() 的定义更改为

    Tensor matmul(const Tensor& rhs); // <-- const added
    

    除非matmul 修改rhs,否则它确实不应该采用非常量引用。参数是非常量引用确实是该示例不起作用的原因。所以,你找到了正确的解决方案。

    调用thisat()at(left) 有效。

    那是因为matmul 仍然是一个非常量成员函数。然而,这有意义吗?就像没有修改rhs的函数一样,它是否修改了*this?我打赌不会。并且鉴于该函数不是 const,您不能这样做:

    const Tensor tensor;
    Tensor tensor3 = tensor.matmul(some_other_tensor);
    

    这种限制有意义吗?应该不会吧。

    因此,您可能应该将其设为 const 成员函数。这也会阻止at*this 一起工作。

    我尝试将所有at()函数的输入参数设置为const,但没有成功。

    这没有帮助。您必须做的是使其成为 const 成员函数。但要做到这一点,它必须返回一个 const 引用。

    但是,您可能还需要返回非常量引用的 at。那么如何解决。我建议检查标准库如何处理这个问题。我们以std::vector为例:

    reference       at( size_type pos );
    const_reference at( size_type pos ) const;
    

    你会看那个。有两个重载。一个常量和另一个非常量。您可以在您的张量类中遵循此示例。

    【讨论】:

    • 这很有帮助,是的...非常感谢! “问题”是我现在有 12 个 at() 函数,原来的 6 个非常量函数,现在是 6 个 const 函数。不幸的是,我的原始问题已编辑,删除了我的课程。我看不出这个新的struct 定义如何与我的班级匹配,因为其中一个问题是支持可变数量的维度,这需要我拥有6 个原始at() 函数。我知道可以用variadic templates 解决,但我就是想不通。
    • @Christian 我的第二次编辑让事情变得更清晰了吗?
    • @JosephSible-ReinstateMonica 赞赏!我用所有 es 工作的代码 sn-p 编辑了问题。我只觉得它会导致很多重复的代码。作为一个附带问题:有没有使用struct 而不是class 的原因?
    • @Christian 此处问题中的示例代码应该是最少的。由于成员为private 与此问题无关,因此使用struct 使其更小。
    【解决方案2】:

    您的方法 at 不是 const。它会修改张量吗?

    如果不是,你可以这样说: T&amp; at(std::size_t d1, std::size_t d2) const; 并可能返回const T&amp;

    但是您遇到的问题是您在 const Tensor 上调用非常量成员函数 (at)

    【讨论】:

    • 添加每个 at() 函数的 const 版本有效。但现在我有 6 对 at() 函数和相当多(几乎)重复的代码。我想知道这是否可以优化。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-13
    • 2017-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多