lỗi: chuyển xxx thành đối số 'this' của xxx loại bỏ vòng loại


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;
}

Trong dòng:

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

Nó đưa ra một lỗi rằng:

../main.cpp:35: lỗi: chuyển 'const StudentT' thành 'đối số' này của 'int StudentT :: getId ()' loại bỏ vòng loại

../main.cpp:35: lỗi: chuyển 'const StudentT' thành 'đối số' này của 'std :: string StudentT :: getName ()' loại bỏ vòng loại

Có gì sai với mã này? Cảm ơn bạn!


13
Dòng 35 trong đoạn mã của bạn ở đâu?
Trong silico

118
Tôi ước GCC sẽ cải thiện thông báo lỗi này, ví dụ: "loại bỏ vòng loại" -> "phá vỡ tính chính xác"
jfritz42

13
@ jfritz42: Sẽ gây nhầm lẫn cho trường hợp nó bị loại bỏvolatile
PlasmaHH

3
@PlasmaHH thông báo lỗi sẽ được chia thành "phá vỡ tính chính xác" và "phá vỡ tính chính xác dễ bay hơi". Bây giờ, không nhiều người sẽ nghĩ về điều gì đó không ổn định
Caleth

Câu trả lời:


524

Các đối tượng trong std::setđược lưu trữ như const StudentT. Vì vậy, khi bạn cố gắng gọi getId()với constđối tượng, trình biên dịch sẽ phát hiện ra một vấn đề, chủ yếu là bạn đang gọi một hàm thành viên không phải là const trên đối tượng const không được phép vì các hàm không phải thành viên không tạo ra KHUYẾN MÃI không sửa đổi đối tượng; vì vậy trình biên dịch sẽ đưa ra một giả định an toàngetId() có thể cố gắng sửa đổi đối tượng nhưng đồng thời, nó cũng thông báo rằng đối tượng là const; Vì vậy, bất kỳ nỗ lực để sửa đổi đối tượng const nên là một lỗi. Do đó trình biên dịch tạo ra một thông báo lỗi.

Giải pháp rất đơn giản: làm cho các hàm const là:

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

Điều này là cần thiết bởi vì bây giờ bạn có thể gọi getId()getName()trên các đối tượng const là:

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
}

Là một sidenote, bạn nên thực hiện operator<như:

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

Lưu ý các tham số hiện đang consttham khảo.


3
Như một lời giải thích rõ ràng. Cảm ơn. Nhưng tôi tự hỏi về đoạn mã cuối cùng của bạn. Tại sao sử dụng tham chiếu trong tham số hàm? const StudentT & s1, const StudentT & s2?
Rafael Adel

2
@RafaelAdel: Bạn sử dụng tài liệu tham khảo để tránh sao chép constkhông cần thiết và vì hàm không cần sửa đổi đối tượng, do đó, việc constthực thi điều này vào thời gian biên dịch.
Nawaz

90

Các hàm thành viên không sửa đổi thể hiện của lớp nên được khai báo là const:

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

Bất cứ khi nào bạn thấy "loại bỏ vòng loại", nó sẽ nói về consthoặc volatile.


2
@Fred - Bạn có nghĩ rằng đó chắc chắn là một yêu cầu để thêm bộ sửa đổi const cho các hàm thành viên không sửa đổi thể hiện của lớp không? Có một số lý do khác cho lỗi trong trường hợp này? Tôi nghi ngờ điều đó bởi vì trong hầu hết các getters tôi viết, tôi không thêm các sửa đổi const cho nó.
Mahesh

@Mahesh: Vâng, đó là một phần của sự đúng đắn . Tôi không chắc chắn nơi constnày đến từ đây, nhưng tôi nghi ngờ việc settrả lại một tham chiếu const từ trình vòng lặp để ngăn trường hợp thay đổi và do đó làm mất hiệu lực tập hợp.
Fred Larson

@Mahesh: Sẽ không vượt qua đánh giá mã của tôi. Tôi có một đồng nghiệp gọi tôi là "const-could". 8v) Thay đổi điều đó foo obj;thành const foo obj;một lần và xem điều gì sẽ xảy ra. Hoặc chuyển một consttham chiếu đến a foo.
Fred Larson

3
@Mahesh: Giống như tôi đã nói - nếu các yếu tố trong a setbị thay đổi, thứ tự có thể bị đảo lộn và sau đó tập hợp không còn hiệu lực nữa. Trong một map, chỉ có chìa khóa là const. Trong một set, toàn bộ đối tượng thực sự là chìa khóa.
Fred Larson

1
@Mahesh: const là cần thiết nếu không bạn không thể gọi chúng bằng các đối tượng const. xem chức năng f()trong câu trả lời của tôi
Nawaz

5

Trên thực tế, tiêu chuẩn C ++ (tức là bản nháp C ++ 0x ) nói (tnx với @Xeo & @Ben Voigt vì đã chỉ ra điều đó cho tôi):

23.2.4 Các thùng chứa kết hợp
5 Đối với tập hợp và nhiều loại, loại giá trị giống như loại khóa. Đối với bản đồ và multimap, nó bằng cặp. Chìa khóa trong một container kết hợp là bất biến.
6 iterator của một container kết hợp thuộc loại iterator hai chiều. Đối với các thùng chứa kết hợp trong đó loại giá trị giống với loại khóa, cả iterator và const_iterator đều là các iterator không đổi. Không xác định được liệu iterator và const_iterator có cùng loại hay không.

Vì vậy, việc thực hiện Dinkumware VC ++ 2008 bị lỗi.


Câu trả lời cũ:

Bạn đã gặp lỗi đó vì trong một số triển khai của std lib, set::iteratornó giống nhưset::const_iterator .

Ví dụ libstdc ++ (giao hàng với g ++) có nó (xem ở đây để biết toàn bộ mã nguồn):

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

Và ở SGI tài liệu có ghi:

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.)

Mặt khác, VC ++ 2008 Express biên dịch mã của bạn mà không phàn nàn rằng bạn đang gọi các phương thức không const trên set::iterators.


2

Hãy để tôi đưa ra một ví dụ chi tiết hơn. Theo cấu trúc dưới đây:

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;
    }
};

nhập mô tả hình ảnh ở đây

Như bạn thấy ở trên, IDE (CLion), sẽ đưa ra lời khuyên Non-const function 'getCount' is called on the const object. Trong phương thức add countđược khai báo là đối tượng const, nhưng phương thức getCountkhông phải là phương thức const, vì vậy count.getCount()có thể thay đổi các thành viên trongcount .

Biên dịch lỗi như dưới đây (thông điệp cốt lõi trong trình biên dịch của tôi):

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

Để giải quyết vấn đề trên, bạn có thể:

  1. thay đổi phương pháp uint32_t getCount(){...} thành uint32_t getCount() const {...}. Vì vậy, count.getCount()sẽ không thay đổi các thành viên trong count.

hoặc là

  1. thay đổi uint32_t add(const Count& count){...} để uint32_t add(Count& count){...}. Vì vậy, countđừng quan tâm đến việc thay đổi thành viên trong đó.

Đối với vấn đề của bạn, các đối tượng trong std :: set được lưu dưới dạng const StudentT, nhưng phương thức getIdgetNamekhông phải là const, do đó bạn đưa ra lỗi trên.

Bạn cũng có thể thấy câu hỏi này Ý nghĩa của 'const' cuối cùng trong một khai báo hàm của một lớp? để biết thêm chi tiết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.