【问题标题】:error: passing 'const S' as 'this' argument discards qualifiers错误:将“const S”作为“this”参数传递会丢弃限定符
【发布时间】:2021-11-10 17:01:06
【问题描述】:

这是从大型代码库移植而来的问题的简化版本。我已经解决了这个问题,但我不喜欢我解决它的方式。

无法编译的有问题的代码是我开始的:

#include <iostream>
#include <cstdlib>
#include <vector>
#include <cassert>
#include <algorithm>
#include <cmath>
#include <array>
#include <utility>
#include <set>
#include <functional>

class S {
  public:
    int a;
    int b;
    mutable int c;
    void set_c() { c = 222; }
};

struct cmp
{
  bool operator()(const S& lhs, const S& rhs) const
  {
    return !(lhs.a == rhs.a && lhs.b == rhs.b);
  }
};

class core {
    public: 
      std::set<S, cmp> set_of_S;
      std::function<void()> f;
    
      void set_fn() {
        f = [this]() {
            auto it = set_of_S.begin();
            it->set_c();
        };
      }
};

int main()
{
    core core;
    
    S a {.a = 2, .b = 3, .c = 0};
    S b {.a = 2, .b = 3, .c = 0};
    S c {.a = 2, .b = 4, .c = 0};
    
    core.set_of_S.insert(a);
    core.set_of_S.insert(b);
    core.set_of_S.insert(c);

    core.set_fn();
    core.f();
    
    std::cout << core.set_of_S.size() << '\n';
}

编译错误是:

prog.cc: In lambda function:
prog.cc:37:23: error: passing 'const S' as 'this' argument discards qualifiers [-fpermissive]
             it->set_c();

好的,有道理。正如有些人告诉我的那样,您应该使用关键字mutable,因为this 没有被捕获为const,并且迭代器it 现在应该是可修改的(或者至少是我所期望的):

      void set_fn() {
        f = [this]() mutable {
            auto it = set_of_S.begin();
            it->set_c();
        };
      } 

这不会编译。这部分对我来说没有意义。所以成员函数不能修改捕获的this 在 lambda 中,但如果你尝试直接在 lambda 编译器中修改S::c 认为没关系。什么?对我来说没有意义。

当我改变时:

void set_c() { c = 222; }

void set_c() const { c = 222; }

它最终会编译,但我不喜欢这个解决方案,因为我们不得不修改原始函数签名,只是因为 lambda 不接受它并且它使其可读性降低。我将 lambdas 视为一种工具,而不是你必须设计的东西。我尝试将mutable 关键字放在各处,但无法编译。而且我认为应该有一种方法可以允许成员函数在 lambda 中修改它自己的状态。

是我遗漏了什么还是这是一个编译器错误?

这里是 wandbox 中有问题的代码:https://wandbox.org/permlink/qzFMW6WIRiKyY3Dj

我知道有人问过这个问题:error: passing xxx as 'this' argument of xxx discards qualifiers,但答案不会讨论使用mutable,据我了解应该可以解决这类情况。

【问题讨论】:

  • 你不修改指针this,所以mutable这个lambda没用。
  • 不是错误。 std::set&lt;S, cmp&gt;::begin() 返回 constant iterator
  • @drew-dormann 如果取消注释it-&gt;set_c(),为什么it-&gt;c = 300 会起作用? wandbox.org/permlink/O9vFnhEO1RKZ9YjX
  • @OVOT it-&gt;c 有效,因为 c 是可变的。但是it-&gt;set_c() 不会,因为set_c 是一个非常量函数,所以不能在 const 对象上调用它
  • 告诉编译器的方法是将函数标记为const,即“可以在 const 对象上调用此函数”

标签: c++ function c++11 lambda


【解决方案1】:

std::set&lt;T&gt; 的元素不可修改 - set_of_S.begin() 返回一个常量迭代器:cppreference

因为 iterator 和 const_iterator 都是常量迭代器(实际上可能是同一类型),所以不可能通过这些成员函数中的任何一个返回的迭代器来改变容器的元素 [begin/@987654325 @]。

也就是说迭代器it指向的元素是const,所以不能在它上面调用set_c这样的非常量函数。 it-&gt;c = 300 仍然有效,因为您已使 c 可变。它与您调用的 lambda 是否可变无关。

【讨论】:

  • 是的,也许标准是这样说的。但是,使set_c const 函数实际上允许您修改迭代器指向的值,这不是很讽刺吗。我认为这是非常不直观的。不过,您确实认为 lambda 与这里无关。
  • 您可以在 C++ 中以多种方式自取其辱。您告诉编译器 c 是可变的,因此让您在 const 函数中更改它没有问题。在这种情况下,这没关系,因为 c 不是该集合比较的一部分,因此您不会破坏任何东西。
猜你喜欢
  • 2015-01-13
  • 1970-01-01
  • 1970-01-01
  • 2018-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-16
  • 1970-01-01
相关资源
最近更新 更多