Запобігання прийому функції const std :: string & від прийняття 0


97

Варто тисячі слів:

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

Компілятор не скаржиться при передачі числа 0 оператору дужок, що приймає рядок. Натомість це збирається та виходить з ладу перед входом у метод із:

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

Довідково:

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

Моя здогадка

Компілятор неявно використовує std::string(0)конструктор для введення методу, який викликає ту ж проблему (google, наведена вище помилка) без поважних причин.

Питання

Чи все-таки виправити це на стороні класу, тому користувач API цього не відчуває і помилка виявляється під час компіляції?

Тобто додавання перевантаження

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

не є хорошим рішенням.


2
Скомпільований код, викидаючи виняток у Visual studio на ohNo [0], за винятком "0xC0000005: Доступ до місця зчитування з порушенням 0x00000000"
TruthSeeker

5
Заявіть про приватне перевантаження, operator[]()яке приймає intаргумент, і не визначайте його.
Петро

2
@Peter Хоча зробити примітку це компоновщик помилка, яка все ще краще , ніж я.
кабанус

5
@kabanus У наведеному вище сценарії це буде помилка компілятора , оскільки оператор приватний! Помилка лінкера, лише якщо його називають у класі ...
Аконкагуа

5
@Peter Це особливо цікаво в ситуаціях , коли немає C ++ 11 не доступно - і вони дійсно існують навіть сьогодні ( на самом деле я в проекті, щоб мати справу з, і я пропускаю в значній мірі деякі з нових можливостей ... ).
Аконкагуа

Відповіді:


161

Причина std::string(0)дійсна, пояснюється тим, 0що є константою нульового покажчика. Так 0 відповідає струнному конструктору, який бере вказівник. Тоді код виконує передумову, на яку не можна передавати нульовий покажчик std::string.

Тільки буквальне 0було б інтерпретуватися як нульова константа вказівника, якби це значення часу запуску у intви не мали б цієї проблеми (тому що тоді розв’язання перевантаження буде шукати intконверсію замість цього). Також це не є буквальною 1проблемою, оскільки 1не є константою нульового покажчика.

Оскільки це проблема часу компіляції (буквально недійсних значень), ви можете впізнати її під час компіляції. Додати перевантаження цієї форми:

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

std::nullptr_tє тип nullptr. І це буде відповідати будь-який нульовий константних покажчик, будь то 0, 0ULLабо nullptr. А оскільки функція видалена, це призведе до помилки часу компіляції під час вирішення перевантаження.


Це, безумовно, найкраще рішення, повністю забув, що я можу перевантажити покажчик NULL.
кабанус

у Visual Studio навіть "ohNo [0]" викидає нульове значення виключення. Чи означає це специфічне для класу std :: string?
TruthSeeker

@pmp Що кидається (якщо що-небудь) - це конкретна реалізація, але суть полягає в тому, що рядок є вказівником NULL у всіх них. За допомогою цього рішення ви не потрапите до частини винятку, воно буде виявлено під час компіляції.
кабанус

18
@pmp - Передача нульового покажчика std::stringконструктору 's не використовується стандартом C ++. Це невизначена поведінка, тому MSVC може робити все, що завгодно (наприклад, кидати виняток).
StoryTeller - Невідповідна Моніка

26

Один із варіантів - оголосити про privateперевантаження того, operator[]()що приймає інтегральний аргумент, а не визначати його.

Цей параметр буде працювати з усіма стандартами C ++ (з 1998 р.), На відміну від параметрів, void operator[](std::nullptr_t) = deleteякі є чинними від C ++ 11.

Створення operator[]()в privateелементі викличе діагностуються помилку на вашому прикладі ohNo[0], якщо цей вислів не використовуються в функції члена або friendкласу.

Якщо це вираження використовується з функції-члена або friendкласу, код буде компілюватися, але - оскільки функція не визначена - як правило, збірка не вийде (наприклад, помилка лінкера через невизначену функцію).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.