C++ Конфликт функции преобразования и перегруженного оператора взятия индекса
Доброго времени суток!
Возникла такая проблема: появилась нужда сотворить самописный класс String, который будет поддерживать взятие индекса (возвращает char) и который можно будет неявно приводить к типу char*.
Проблема возникает тогда, когда всё организовано и пытаюсь взять индекс. Выдаёт такое сообщение (ошибка компиляции):
существует несколько операторов "[]", соответствующих этим операндам:
встроенный оператор "pointer-to-object[integer]"
функцию "String::operator[](size_t index) const"
типы операндов: String [ int ]
Как я понял, он в попытках взять элемент под индексом видит два пути:
неявно преобразовать
Stringкchar*и от него уже взять элемент под каким-либо индексомиспользовать перегруженный оператор
Подскажите, пожалуйста, как сделать так, чтобы он однозначно использовал при взятии индекса конкретно перегруженный operator[]
Вот код класса (неполный, ибо класс достаточно большой, но уверяю, проблема в тех строчках):
String.h:
#ifndef CLASS_STRING
#define CLASS_STRING
class String {
public:
String(const char* str);
char& operator[](size_t index) const;
operator char* () const;
private:
size_t size_;
size_t capacity_;
char* pointer_;
};
#endif
String.cpp:
#include "String.h"
#include <iostream>
String::String(const char* str)
{
size_ = 0;
while (str[size_] != '\0') {
++size_;
}
capacity_ = size_ + 1;
pointer_ = new char[capacity_];
for (unsigned int i = 0; i < capacity_; ++i) {
pointer_[i] = str[i];
}
}
char& String::operator[](size_t index) const
{
if (index >= size_) {
throw std::out_of_range("IndexError: String index out of range");
}
return pointer_[index];
}
String::operator char* () const
{
return pointer_;
}
main.cpp:
#include <iostream>
#include "String.h"
int main() {
String name = "123";
std::cout << name[0] << std::endl;
}
Заранее благодарю
P.S. на скриншотах основной файл Pets.cpp - отголоски старого проекта
Ответы (1 шт):
Ок, давайте попробуем разобраться, что же там происходит. Объявив оператор каста к указателю на строку, Вы открыли дыру в безопасности типов. Дело в том, что у указателей по умолчанию есть объявленный неявный оператор [] - operator[](const char *, int). Видите главную его особенность? там int, а у вас - size_t.
size_t - это "магический тип", то есть тип, который компилятор сам себе определяет внутри, главное, что бы он соответствовал стандарту.
std::size_t can store the maximum size of a theoretically possible object of any type (including array).
int это тоже "магический тип". да да, это один из самых старых магических типов и компилятор даже по умолчанию, когда видит отсутствие типа, считает, что там int.
По очевидным причинам, скорее всего на 32 битной платформе это будет 32битное число, а на 64 битной - 64битное (еще раз - скорее всего. компилятор, зная особенности конкретной платформы, может решить по своему усмотрению, но на распространённых платформах это скорее всего так).
А вот int не повезло. на 64битной платформе он 32битный. И там он совпадает с size_t по размеру. и компилятор не может решить, какую с перегрузок ему выбрать - пользовательский оператор [] или оператор приведения к char* и у него уже встроенный оператор [] (как по мне, то это странная идея, но разработчикам стандарта виднее). А вот на 64битной платформе этой проблемы нет - там типы имеют разный размер и проблемы не возникает.
когда же написать вот так name[0u], то мы явно указываем, что у нас там целое беззнаковое, а это автоматом заставляет использовать перегрузку для size_t
Такая перегрузка ещё и опасна сама по себе. Например, можно так
#include <iostream>
void foo(char* s) {
}
class String {
public:
operator char*() { return nullptr;}
};
int main()
{
String s;
foo(s);
}
Как по мне, это супер-опасный код. функция foo может поменять внутри все.
Видимо это все и привело к тому, что в стандартной библиотеке это сделали явным - c_str().
