ข้อผิดพลาด: ส่ง xxx เป็นอาร์กิวเมนต์ 'this' ของ xxx ยกเลิกตัวระบุ


457
#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' เป็นอาร์กิวเมนต์ 'this' ของ 'int StudentT :: getId ()' ยกเลิกตัวระบุ

../main.cpp:35: ข้อผิดพลาด: ผ่าน 'const StudentT' เป็นอาร์กิวเมนต์ 'this' ของ 'std :: string StudentT :: getName ()' ยกเลิกตัวระบุ

เกิดอะไรขึ้นกับรหัสนี้ ขอบคุณ!


13
บรรทัด 35 ในโค้ดของคุณอยู่ที่ไหน
ใน silico

117
ฉันหวังว่า GCC จะปรับปรุงข้อความแสดงข้อผิดพลาดนี้เช่น "ยกเลิกตัวระบุ" -> "แบ่งความถูกต้องของ const"
jfritz42

13
@ jfritz42: จะเกิดความสับสนในกรณีที่มันถูกทิ้งไว้volatile
PlasmaHH

3
@PlasmaHH ข้อความแสดงข้อผิดพลาดจะถูกแบ่งออกเป็น "การแบ่งความถูกต้อง const" และ "การแบ่งความถูกต้องระเหย" ตอนนี้ไม่กี่คนที่จะคิดเกี่ยวกับสิ่งที่ถูกต้องระเหย
Caleth

คำตอบ:


524

วัตถุในการจะถูกเก็บเป็นstd::set const StudentTดังนั้นเมื่อคุณพยายามที่จะเรียกgetId()กับconstวัตถุคอมไพเลอร์ตรวจพบปัญหาส่วนใหญ่ที่คุณกำลังเรียกฟังก์ชั่นไม่ใช่สมาชิก const บนวัตถุ const ซึ่งไม่ได้รับอนุญาตเพราะฟังก์ชั่นไม่ใช่สมาชิก const ทำให้ไม่มีสัญญาว่าจะไม่ปรับเปลี่ยนวัตถุนั้น ดังนั้นคอมไพเลอร์จะทำให้สมมติฐานที่ปลอดภัยที่getId()อาจพยายามแก้ไขวัตถุ แต่ในเวลาเดียวกันก็สังเกตเห็นว่าวัตถุเป็น const; ดังนั้นความพยายามใด ๆ ในการแก้ไขวัตถุ const ควรเป็นข้อผิดพลาด ดังนั้นคอมไพเลอร์สร้างข้อความแสดงข้อผิดพลาด

วิธีแก้ปัญหาง่าย: ทำให้ฟังก์ชั่น const เป็น:

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

สิ่งนี้จำเป็นเพราะตอนนี้คุณสามารถเรียกใช้getId()และgetName()บนวัตถุ const ได้ดังนี้

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
}

ในฐานะ sidenote คุณควรใช้operator<เป็น:

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

ขณะนี้constการอ้างอิงพารามิเตอร์หมายเหตุ


3
คำอธิบายที่ชัดเจน ขอบคุณ แต่ฉันสงสัยเกี่ยวกับข้อมูลโค้ดสุดท้ายของคุณ เหตุใดจึงใช้การอ้างอิงในพารามิเตอร์ฟังก์ชัน const StudentT & s1, const StudentT & s2?
Rafael Adel

2
@RafaelAdel: คุณใช้การอ้างอิงเพื่อหลีกเลี่ยงการคัดลอกที่ไม่จำเป็นและconstเนื่องจากฟังก์ชั่นไม่จำเป็นต้องแก้ไขวัตถุดังนั้นจึงconstบังคับใช้ในเวลารวบรวม
Nawaz

90

ฟังก์ชันสมาชิกที่ไม่ได้แก้ไขอินสแตนซ์ของคลาสควรถูกประกาศเป็นconst:

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

เมื่อใดก็ตามที่คุณเห็น "ทิ้งบ่น" ก็พูดคุยเกี่ยวกับหรือconstvolatile


2
@Fred - คุณคิดว่าเป็นความต้องการที่แน่นอนในการเพิ่ม const modifiers ให้กับฟังก์ชั่นสมาชิกที่ไม่ได้แก้ไขอินสแตนซ์ของคลาสหรือไม่? ในกรณีนี้มีเหตุผลอื่นอีกหรือไม่? ฉันสงสัยเพราะส่วนใหญ่ของ getters ฉันเขียนฉันจะไม่เพิ่มตัวดัดแปลง const มัน
Mahesh

@Mahesh: ใช่มันเป็นส่วนหนึ่งของความถูกต้อง const ฉันไม่แน่ใจว่าที่constมาจากที่นี่ แต่ฉันสงสัยว่าsetจะส่งคืนการอ้างอิง const จากตัววนซ้ำเพื่อป้องกันไม่ให้อินสแตนซ์เปลี่ยนและทำให้ชุดใช้งานไม่ได้
Fred Larson

@Mahesh: จะไม่ผ่านการตรวจสอบรหัสของฉัน ฉันมีเพื่อนร่วมงานที่อ้างถึงฉันเป็น "const-can" 8v) เปลี่ยนfoo obj;เป็นconst foo obj;ครั้งเดียวและดูว่าเกิดอะไรขึ้น หรือผ่านการอ้างอิงถึงconst foo
Fred Larson

3
@Mahesh: มันเหมือนที่ฉันพูด - หากองค์ประกอบใน a setเปลี่ยนไปคำสั่งสามารถทำให้อารมณ์เสียแล้วชุดจะไม่ถูกต้องอีกต่อไป ในเท่านั้นที่สำคัญคือmap constในsetวัตถุทั้งหมดเป็นกุญแจสำคัญจริงๆ
Fred Larson

1
@Mahesh: const มีความจำเป็นมิฉะนั้นคุณจะไม่สามารถเรียกมันด้วยวัตถุ const ดูฟังก์ชั่นf()ในคำตอบของฉัน
Nawaz

5

ที่จริงแล้วมาตรฐาน C ++ (เช่นC ++ 0x ฉบับร่าง ) พูดว่า (tnx ถึง @Xeo & @Ben Voigt สำหรับการชี้ให้ฉันเห็น):

23.2.4คอนเทนเนอร์ที่เชื่อมโยง
5สำหรับชุดและมัลติเซ็ตชนิดของค่าจะเหมือนกับคีย์ประเภท สำหรับ map และ multimap มันเท่ากับการจับคู่ คีย์ในคอนเทนเนอร์ที่เชื่อมโยงนั้นไม่เปลี่ยนรูป
6 ตัววนซ้ำของคอนเทนเนอร์แบบเชื่อมโยงเป็นหมวดหมู่ตัววนซ้ำแบบสองทิศทาง สำหรับคอนเทนเนอร์ที่เชื่อมโยงที่ชนิดค่าเหมือนกันกับชนิดของคีย์ทั้งตัววนซ้ำและ const_iterator เป็นตัววนซ้ำคงที่ มันไม่ได้ระบุว่า iterator และ const_iterator เป็นประเภทเดียวกันหรือไม่

ดังนั้นการใช้งาน DTCCELL แวร์เสียหายของ VC ++ 2008


คำตอบเก่า:

คุณมีข้อผิดพลาดว่าเป็นเพราะในบางการใช้งานมาตรฐาน lib เป็นเช่นเดียวกับ set::iteratorset::const_iterator

ตัวอย่างเช่น libstdc ++ (มาพร้อมกับ g ++) มีอยู่ (ดูที่นี่สำหรับรหัสต้นฉบับทั้งหมด):

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

และในเอกสารของ SGI ก็ระบุว่า:

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 รวบรวมรหัสของคุณโดยไม่บ่นว่าคุณกำลังโทรหาวิธีการที่ไม่ใช่ const ในset::iterators


2

ขอยกตัวอย่างรายละเอียดเพิ่มเติม ตามโครงสร้างด้านล่าง:

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& count){...} uint32_t add(Count& count){...}ดังนั้นcountอย่าสนใจเปลี่ยนสมาชิกในวง

สำหรับปัญหาของคุณวัตถุใน std :: set จะถูกเก็บเป็น const StudentT แต่เป็นวิธีการgetIdและgetNameไม่ใช่ const ดังนั้นคุณจึงให้ข้อผิดพลาดด้านบน

คุณสามารถเห็นคำถามนี้ความหมายของ 'const' สุดท้ายในการประกาศฟังก์ชันของคลาสหรือไม่ สำหรับรายละเอียดเพิ่มเติม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.