2015-02-10 2 views
18

Мой коллега сегодня обнаружили очень тонкую ошибку в коде, который в основном пошли как это:Почему строка поддерживает `operator = (char)`?

double d = 65; 
std::string s = "Hello world"; 

// .. somewhere later, accidentally assigning to s instead of a similarly 
// named numerical variable. 
s = d; 

// s is now 'A' 

Причина, по которой эта ошибка может произойти, я обнаружил, что std::basic_string<_Elem> имеет оператор присваивания

_Myt& operator=(_Elem _Ch) 
{ // assign 1 * _Ch 
    return (assign(1, _Ch)); 
} 

Теперь компилятор действительно не жалуется (много, он излучает предупреждение о сужении конверсии, если уровень достаточно высок). Кажется, мы поймали эту ошибку достаточно рано, чтобы она не наносила большого ущерба, но мне было интересно, почему это разрешено. В конце концов, я не могу написать

std::string s = 65; 

, потому что std::string не имеет (п неявное) конструктор, который принимает char. Не было бы безопаснее сделать это явное преобразование, которое заставляет вас писать

std::string s = string('A'); 

и что бы запретить присвоение одной _Elem (char).

Есть ли какие-либо основания для этого оператора присваивания? В то же коллеги правильно заметили,

double d; 
char c = d; 

допускается, тогда как

int* p = d; 

не (для любого размера указателя) - по-видимому, потому что неявное преобразование от числа к указателю считается опасным. На самом деле, похоже, он даже превратился в C++ 11, который, насколько я видел, пытается быть довольно строгим и полезным в управлении типом данных.

+10

Вот почему вы всегда устанавливаете предупреждения высоко! «как двойник, так и символ в четырех байтах» не уверены, что это значит. Они разных размеров. –

+4

@NeilKirk Почему не установлен по умолчанию установленный уровень предупреждения? Никто не хочет, чтобы их компилятор был первым, кто похоронил код своих клиентов в море предупреждений обо всех своих плохих практиках? –

+1

«это даже похоже на то, что он превратился в C++ 11» - потому что удаление его сломало бы 30-летний код устаревшего кода, так что не так легко сделать. –

ответ

16

Кто-то 30 лет назад написал библиотеку, из которой было импортировано std::string, и подумал, что это хорошая идея.

Перед стандартизацией он не был удален.

С этого момента удаление его может привести к повреждению устаревшего кода, что является издержкой.

Атрибут deprecated был добавлен недавно в C++, что позволяет стандартным образом сообщить пользователю о функции, которая вскоре исчезнет. Никто не сумел сделать его устаревшим, что является разумным шагом, необходимым, прежде чем он будет удален (просто удалить его было бы грубо). Я призываю вас сделать такое предложение.

+2

Я бы предложил добавить атрибут [[устаревший]] к этой функции-члену и удалить его с помощью C++ 17. В конце концов, его можно легко заменить назначением на список с фиксированным набором, например. 's = {'5'};' – Columbo

+2

@Columbo Удаление его с помощью C++ 17 кажется чрезмерно агрессивным: оно должно быть устарело, по крайней мере, для одной стандартной версии перед ее удалением. Я бы утвердил '[[устарело]]' для C++ 17, а затем предложил удалить его в C++ 2x или C++ 2y. – Yakk

+2

Упс! Виноват. Я хотел сказать «по стандарту после C++ 17». Ясно, что мы можем ввести только атрибут в C++ 17. :) – Columbo