Стоит тысячи слов:
#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");
}
не очень хорошее решение
c++
string
std
implicit-conversion
kabanus
источник
источник
operator[]()
которая принимаетint
аргумент, и не определяйте его.Ответы:
Причина
std::string(0)
действительна, потому0
что является константой нулевого указателя. Таким образом, 0 соответствует строковому конструктору, принимающему указатель. Затем код запускается из-за предусловия, на которое нельзя передавать нулевой указательstd::string
.Только литерал
0
будет интерпретироваться как константа нулевого указателя, если бы это было значение времени выполнения, то уint
вас не было бы этой проблемы (потому что тогда при разрешении перегрузкиint
вместо этого нужно было бы искать преобразование). И не является литералом1
проблема, потому что1
не является константой нулевого указателя.Поскольку это проблема времени компиляции (буквальные недопустимые значения), вы можете поймать ее во время компиляции. Добавьте перегрузку этой формы:
std::nullptr_t
это типnullptr
. И это будет соответствовать любой постоянной нулевой указатель, будь то0
,0ULL
илиnullptr
. А так как функция удалена, она вызовет ошибку времени компиляции во время разрешения перегрузки.источник
std::string
конструктор не допускается стандартом C ++. Это неопределенное поведение, поэтому MSVC может делать все что угодно (например, генерировать исключение).Одним из вариантов является объявление
private
перегрузкиoperator[]()
которая принимает интегральный аргумент, и не определять его.Эта опция будет работать со всеми стандартами C ++ (с 1998 года), в отличие от таких опций, как
void operator[](std::nullptr_t) = delete
начиная с которые действительны в C ++ 11.Создание
operator[]()
вprivate
элементе вызовет диагностируемую ошибку на вашем примереohNo[0]
, если это выражение не используются в функции члена илиfriend
класса.Если это выражение используется из функции-члена или
friend
класса, код будет компилироваться, но - поскольку функция не определена - обычно сборка завершится неудачно (например, ошибка компоновщика из-за неопределенной функции).источник