Ngăn chặn chức năng lấy const std :: string & từ chấp nhận 0


97

Đáng giá ngàn lời nói:

#include<string>
#include<iostream>

class SayWhat {
    public:
    SayWhat& operator[](const std::string& s) {
        std::cout<<"here\n"; // To make sure we fail on function entry
        std::cout<<s<<"\n";
        return *this;
    }
};

int main() {
    SayWhat ohNo;
    // ohNo[1]; // Does not compile. Logic prevails.
    ohNo[0]; // you didn't! this compiles.
    return 0;
}

Trình biên dịch không phàn nàn khi chuyển số 0 cho toán tử ngoặc chấp nhận chuỗi. Thay vào đó, điều này biên dịch và thất bại trước khi vào phương thức với:

terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_S_construct null not valid

Để tham khảo:

> g++ -std=c++17 -O3 -Wall -Werror -pedantic test.cpp -o test && ./test
> g++ --version
gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)

Tôi đoán

Trình biên dịch đang ngầm sử dụng hàm std::string(0)tạo để nhập phương thức, điều này dẫn đến cùng một vấn đề (google lỗi ở trên) không có lý do chính đáng.

Câu hỏi

Có cách nào để khắc phục điều này ở phía lớp không, vì vậy người dùng API không cảm thấy điều này và lỗi được phát hiện tại thời điểm biên dịch?

Đó là, thêm một quá tải

void operator[](size_t t) {
    throw std::runtime_error("don't");
}

không phải là một giải pháp tốt.


2
Mã được biên dịch, ném ngoại lệ trong Visual studio tại ohKhông [0] với ngoại lệ "0xC0000005: Truy cập vị trí đọc vi phạm 0x00000000"
TruthSeeker

5
Khai báo tình trạng quá tải riêng tư operator[]()chấp nhận intđối số và không xác định nó.
Peter

2
@Peter Mặc dù lưu ý rằng đó là một lỗi liên kết , vẫn tốt hơn những gì tôi đã có.
kabanus

5
@kabanus Trong kịch bản trên, đây sẽ là lỗi trình biên dịch , vì toán tử là riêng tư! Lỗi liên kết chỉ khi được gọi trong lớp ...
Aconcagua

5
@ Peter Đó là đặc biệt thú vị trong kịch bản mà không có C ++ 11 có sẵn - và những tồn tại ngay cả ngày hôm nay (thực sự tôi đang ở trong một dự án phải đối phó với, và tôi đang thiếu khá nhiều một số tính năng mới ... ).
Aconcagua

Câu trả lời:


161

Lý do std::string(0)là hợp lệ, là do 0hằng số con trỏ null. Vì vậy, 0 khớp với hàm tạo chuỗi lấy một con trỏ. Sau đó, mã chạy afoul của điều kiện tiên quyết mà người ta không thể vượt qua một con trỏ null đến std::string.

Chỉ bằng chữ 0sẽ được hiểu là hằng số con trỏ null, nếu đó là giá trị thời gian chạy trong một intbạn sẽ không gặp vấn đề này (vì khi đó độ phân giải quá tải sẽ tìm kiếm một intchuyển đổi thay thế). Nghĩa đen cũng không phải là 1vấn đề, bởi vì 1không phải là hằng số con trỏ rỗng.

Vì đó là một vấn đề thời gian biên dịch (giá trị không hợp lệ theo nghĩa đen), bạn có thể nắm bắt nó tại thời gian biên dịch. Thêm một quá tải của hình thức này:

void operator[](std::nullptr_t) = delete;

std::nullptr_tlà loại nullptr. Và nó sẽ phù hợp với bất kỳ rỗng trỏ liên tục, có thể là 0, 0ULLhoặc nullptr. Và vì chức năng bị xóa, nó sẽ gây ra lỗi thời gian biên dịch trong quá trình phân giải quá tải.


Đây là giải pháp tốt nhất, hoàn toàn quên tôi có thể quá tải một con trỏ NULL.
kabanus

trong Visual Studio, thậm chí "ohNo [0]" ném ngoại lệ giá trị null. Nó có nghĩa là một triển khai cụ thể của lớp std :: string?
TruthSeeker

@pmp Những gì được ném (nếu có gì) là cụ thể triển khai, nhưng điểm chính là chuỗi là một con trỏ NULL trong tất cả chúng. Với giải pháp này, bạn sẽ không nhận được phần ngoại lệ, nó sẽ được phát hiện tại thời điểm biên dịch.
kabanus

18
@pmp - Việc chuyển một con trỏ null sang hàm tạo std::stringcủa chuẩn không được phép theo tiêu chuẩn C ++. Đó là hành vi không xác định, vì vậy MSVC có thể làm bất cứ điều gì mình thích (như ném ngoại lệ).
Người kể chuyện - Unslander Monica

26

Một tùy chọn là khai báo privatetình trạng quá tải operator[]()chấp nhận một đối số không thể tách rời và không xác định nó.

Tùy chọn này sẽ hoạt động với tất cả các tiêu chuẩn C ++ (1998 trở đi), không giống như các tùy chọn như void operator[](std::nullptr_t) = deletehợp lệ từ C ++ 11.

Làm cho operator[]()một privatethành viên sẽ gây ra một lỗi có thể chẩn đoán trên ví dụ của bạn ohNo[0], trừ khi biểu thức được sử dụng bởi một hàm thành viên hoặc friendcủa lớp.

Nếu biểu thức đó được sử dụng từ một hàm thành viên hoặc friendcủa lớp, mã sẽ biên dịch nhưng - vì hàm này không được xác định - nói chung, quá trình xây dựng sẽ thất bại (ví dụ: lỗi liên kết do hàm không xác định).

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.