【问题标题】:error: passing xxx as 'this' argument of xxx discards qualifiers错误:将 xxx 作为 xxx 的“this”参数传递会丢弃限定符
【发布时间】:2021-03-23 09:53:58
【问题描述】:
#include <iostream>
#include <set>

using namespace std;

class StudentT {

public:
    int id;
    string name;
public:
    StudentT(int _id, string _name) : id(_id), name(_name) {
    }
    int getId() {
        return id;
    }
    string getName() {
        return name;
    }
};

inline bool operator< (StudentT s1, StudentT s2) {
    return  s1.getId() < s2.getId();
}

int main() {

    set<StudentT> st;
    StudentT s1(0, "Tom");
    StudentT s2(1, "Tim");
    st.insert(s1);
    st.insert(s2);
    set<StudentT> :: iterator itr;
    for (itr = st.begin(); itr != st.end(); itr++) {
        cout << itr->getId() << " " << itr->getName() << endl;
    }
    return 0;
}

排队:

cout << itr->getId() << " " << itr->getName() << endl;

它给出一个错误:

../main.cpp:35: 错误:将 'const StudentT' 作为 'int StudentT::getId()' 的 'this' 参数传递会丢弃限定符

../main.cpp:35: 错误:将 'const StudentT' 作为 'std::string StudentT::getName()' 的 'this' 参数传递会丢弃限定符

这段代码有什么问题?谢谢!

【问题讨论】:

  • 代码 sn-p 中的第 35 行在哪里?
  • 我希望 GCC 能改进这个错误信息,例如“丢弃限定符”->“破坏 const 正确性”
  • @jfritz42:对于它丢弃 volatile 的情况会令人困惑
  • @PlasmaHH 错误消息将分为“破坏 const 正确性”和“破坏 volatile 正确性”。现在,没有多少人会考虑 volatile 正确

标签: c++


【解决方案1】:

让我举一个更详细的例子。至于下面的结构:

struct Count{
    uint32_t c;

    Count(uint32_t i=0):c(i){}

    uint32_t getCount(){
        return c;
    }

    uint32_t add(const Count& count){
        uint32_t total = c + count.getCount();
        return total;
    }
};

正如您在上面看到的,IDE(CLion) 将给出提示Non-const function 'getCount' is called on the const object。在add方法中count被声明为const对象,但getCount方法不是const方法,所以count.getCount()可能会改变count中的成员。

编译错误如下(我的编译器中的核心消息):

error: passing 'const xy_stl::Count' as 'this' argument discards qualifiers [-fpermissive]

要解决上述问题,您可以:

  1. 将方法uint32_t getCount(){...}更改为uint32_t getCount() const {...}。所以count.getCount() 不会更改count 中的成员。

  1. uint32_t add(const Count&amp; count){...} 更改为uint32_t add(Count&amp; count){...}。所以count 不关心更换成员。

关于你的问题,std::set中的对象存储为const StudentT,但是方法getIdgetName不是const,所以你给出了上面的错误。

您也可以查看此问题Meaning of 'const' last in a function declaration of a class? 了解更多详情。

【讨论】:

    【解决方案2】:

    std::set 中的对象存储为const StudentT。因此,当您尝试使用 const 对象调用 getId() 时,编译器会检测到问题,主要是您在 const 对象上调用非常量成员函数,这是不允许的,因为非常量成员函数不承诺不会修改对象;所以编译器会做出一个安全的假设,即getId() 可能会尝试修改该对象,但同时它也注意到该对象是常量;所以任何修改 const 对象的尝试都应该是一个错误。因此编译器会生成一条错误消息。

    解决方案很简单:将函数设为:

    int getId() const {
        return id;
    }
    string getName() const {
        return name;
    }
    

    这是必要的,因为现在您可以在 const 对象上调用 getId()getName()

    void f(const StudentT & s)
    {
         cout << s.getId();   //now okay, but error with your versions
         cout << s.getName(); //now okay, but error with your versions
    }
    

    作为旁注,您应该将operator&lt; 实现为:

    inline bool operator< (const StudentT & s1, const StudentT & s2)
    {
        return  s1.getId() < s2.getId();
    }
    

    注意参数现在是const 参考。

    【讨论】:

    • 这么清晰的解释。谢谢。但我想知道你的最后一个代码 sn-p。为什么在函数参数中使用引用? const StudentT &amp; s1, const StudentT &amp; s2 ?
    • @RafaelAdel:您使用引用来避免不必要的复制,而const 因为函数不需要修改对象,所以const 在编译时强制执行此操作。
    【解决方案3】:

    实际上,C++ 标准(即C++ 0x draft)说(@Xeo 和@Ben Voigt 向我指出这一点):

    23.2.4 关联容器
    5 用于设置和多重设置值类型 与密钥类型相同。对于地图 和multimap它等于pair。关联中的键 容器是不可变的。
    6 的迭代器 关联容器是 双向迭代器类别。为了 关联容器的值 类型与键类型相同,两者 iterator 和 const_iterator 是 常量迭代器。未指定 是否迭代器和 const_iterator 是同一类型。

    所以 VC++ 2008 Dinkumware 实现是错误的。


    旧答案:

    您收到该错误是因为在 std lib 的某些实现中,set::iteratorset::const_iterator 相同。

    例如 libstdc++(与 g++ 一起提供)有它(完整源代码请参见here):

    typedef typename _Rep_type::const_iterator            iterator;
    typedef typename _Rep_type::const_iterator            const_iterator;
    

    并且在 SGI 的 docs 中声明:

    iterator       Container  Iterator used to iterate through a set.
    const_iterator Container  Const iterator used to iterate through a set. (Iterator and const_iterator are the same type.)
    

    另一方面,VC++ 2008 Express 编译您的代码,而不会抱怨您在 set::iterators 上调用非 const 方法。

    【讨论】:

      【解决方案4】:

      不修改类实例的成员函数应声明为const

      int getId() const {
          return id;
      }
      string getName() const {
          return name;
      }
      

      每当您看到“丢弃限定符”时,它指的是constvolatile

      【讨论】:

      • @Fred - 你认为绝对需要向不修改类实例的成员函数添加 const 修饰符吗?在这种情况下,错误还有其他原因吗?我对此表示怀疑,因为在我编写的大多数 getter 中,我都没有添加 const 修饰符。
      • @Mahesh:是的,它是const correctness 的一部分。我不确定const 是从哪里来的,但我怀疑set 正在从迭代器返回一个常量引用,以防止实例更改从而使集合无效。
      • @Mahesh:不会通过我的代码审查。我有一位同事称我为“能干的人”。 8v) 将foo obj; 更改为const foo obj; 一次,看看会发生什么。或者将const 引用传递给foo
      • @Mahesh:就像我说的那样——如果set 中的元素发生更改,则顺序可能会被打乱,然后集合不再有效。在map 中,只有键是const。在set 中,整个对象才是真正的关键。
      • @Mahesh: const 是必要的,否则你不能用 const 对象调用它们。在我的回答中查看函数f()
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多