""+ что-то в C++
у меня были действительно причудливые вещи, происходящие в моем коде. Я считаю, что я отследил его до части с надписью "Здесь" (код упрощен, конечно):
std::string func() {
char c;
// Do stuff that will assign to c
return "" + c; // Here
}
всякие вещи будут происходить, когда я пытаюсь cout
результат этой функции. Я думаю, что мне даже удалось получить части базовой документации C++, и многие ошибка сегментирования. Мне ясно, что это не работает в C++ (я прибегал к использованию stringstream
для преобразования в string
сейчас), но я хотел бы знать, почему. После использования много на C#, а не C++, это причинило мне много боли.
3 ответа:
""
- строковый литерал. Те имеют тип массив Nconst char
. Этот конкретный строковый литерал является блок 1const char
, один элемент является нулевым Терминатором.массивы легко распадаются на указатели на первый элемент, например, в выражениях, где требуется указатель.
lhs + rhs
не определяется для массивов какlhs
и целые числа, аrhs
. Но это определяется для указателей как lhs и целых чисел как rhs, с обычной арифметикой указателя.
char
- это интегральный тип данных (т. е. обрабатывается как целое число) на языке ядра C++.==>строковый литерал
+
символ трактуется как указатель+
целое.выражение
"" + c
примерно эквивалентно к:static char const lit[1] = {''}; char const* p = &lit[0]; p + c // "" + c is roughly equivalent to this expression
вы возвращаете a
std::string
. Выражение"" + c
дает a указательconst char
. Конструкторstd::string
что ожидает aconst char*
ожидает, что это будет указатель на массив символов с нулевым завершением.если
c != 0
, то выражение"" + c
приводит к неопределенному поведению:
на
c > 1
, арифметика указателя производит неопределенное поведение. Арифметика указателя определяется только на массивы, и если результат является элементом того же массива.если
char
подписан, тоc < 0
производит неопределенное поведение по той же причине.на
c == 1
указатель арифметические не производит неопределенное поведение. Это особый случай; указание на один элемент после последнего элемента массива разрешено (однако не разрешается использовать то, на что он указывает). Это все еще приводит к неопределенному поведению так какstd::string
конструктор, вызываемый здесь, требует, чтобы его аргумент был указателем на допустимый массив (и строку с нулевым завершением). Элемент one-past-the-last не является частью самого массива. Нарушение этого требования приводит к UB.
что, вероятно, сейчас происходит, так это то, что конструктор
std::string
пытается определить размер строки с нулевым завершением, которую вы передали, путем поиска (первого) символа в массиве, который равен''
:string(char const* p) { // simplified char const* end = p; while(*end != '') ++end; //... }
это либо приведет к нарушению прав доступа, либо строка, которую он создает, содержит "мусор". Также возможно, что компилятор предполагает, что это неопределенное поведение никогда не произойдет, и делает некоторые забавные оптимизации, которые приведут к странному поведению.
кстати, clang++3.5 выдает хорошее предупреждение на этот фрагмент:
предупреждение: добавление 'char' в строку не добавляет к строке [- Wstring-plus-int]
return "" + c; // Here ~~~^~~
Примечание: используйте индексацию массива, чтобы заставить замолчать это предупреждение
есть много объяснений того, как компилятор интерпретирует этот код, но то, что вы, вероятно, хотели знать, что вы сделали неправильно.
Вы, кажется, ожидаете
+
поведениеstd::string
. Проблема в том, что ни один из операндов на самом деле этоstd::string
. C++ смотрит на типы операндов, а не на конечный тип выражения (здесь возвращаемый тип,std::string
) для устранения перегрузки. Он не будет выбиратьstd::string
'S версия+
если он не видитstd::string
.если у вас есть специальное поведение для оператора (либо вы написали его, либо получили библиотеку, которая его предоставляет), это поведение применяется только тогда, когда по крайней мере один из операндов имеет тип класса (или ссылку на тип класса, а также количество пользовательских перечислений).
если ты написал
std::string("") + c
или
std::string() + c
или
""s + c // requires C++14
тогда вы получите
std::string
поведение оператора +.(обратите внимание, что ни один из это на самом деле хорошие решения, потому что все они делают недолговечными
std::string
экземпляры, которых можно избежать с помощьюstd::string(1, c)
)то же самое касается функции. Вот пример:
std::complex<double> ipi = std::log(-1.0);
вы получите ошибку времени выполнения, вместо ожидаемого мнимое число. Это потому, что компилятор понятия не имеет, что он должен использовать сложный логарифм здесь. Перегрузка смотрит только на аргументы, а аргумент является вещественным числом (тип
double
, фактически.)перегрузки операторов являются функциями и подчиняются тем же правилам.
этот оператор return
return "" + c;
действителен. Здесь используется так называемая арифметика указателя. Строковый литерал "" преобразуется в указатель на его первый символ (в данном случае на его конечный ноль), а целочисленное значение, хранящееся в c, добавляется к указателю. Так что результат выражения
"" + c
типа
const char *
класс std:: string имеет конструктор преобразования, который принимает аргумент типа
const char *
. Проблема в том, что этот указатель может указывать на за пределами строкового литерала. Таким образом, функция имеет неопределенное поведение.Я не вижу никакого смысла в использовании этого выражения. Если вы хотите построить строку на основе одного символа, вы можете написать например
return std::string( 1, c );
разница между C++ и C#, что в C# строковые литералы имеют тип System.Строка с перегруженным оператором + для строк и символов (которые являются символами юникода в C#). В C++ строковые литералы являются постоянными символьными массивами и семантикой оператор + для массивов и целых чисел различны. Массивы преобразуются в указатели на их первые элементы и там используется арифметика указателя.
это стандартный класс std:: string, который имеет перегруженный оператор + для символов. Строковые литералы в C++ не являются объектами этого класса, который имеет тип std:: string.