2016-12-24 6 views
2

Я бы предположил, что последние две строки в этом коде должны компилироваться.Может ли std :: string :: c_str() использоваться всякий раз, когда ожидается строковый литерал?

#include "rapidjson/document.h" 

int main(){ 
    using namespace rapidjson ; 
    using namespace std ; 

    Document doc ; 
    Value obj(kObjectType) ; 
    obj.AddMember("key", "value", doc.GetAllocator()) ; //this compiles fine 
    obj.AddMember("key", string("value").c_str(), doc.GetAllocator()) ; //this does not compile! 
} 

Мое предположение было бы неправильным. Одна строка компилируется, а другая - нет.

Метод AddMember имеет несколько вариантов, как задокументировано here, но помимо этого ... почему возврат .c_str() не эквивалентен строковому литералу?

Насколько я понял, где бы ни был принят литерал строки, вы можете пройти string::c_str(), и он должен работать.

PS: Я компиляции с VC++ 2010.

EDIT:
Отсутствие #include <string> не проблема. Он уже включен document.h

Это ошибка:

error C2664: 'rapidjson::GenericValue<Encoding> &rapidjson::GenericValue<Encoding>::AddMember(rapidjson::GenericValue<Encoding> &,rapidjson::GenericValue<Encoding> &,Allocator &)' 
: cannot convert parameter 1 from 'const char [4]' to 'rapidjson::GenericValue<Encoding> &' 
    with 
    [ 
     Encoding=rapidjson::UTF8<>, 
     Allocator=rapidjson::MemoryPoolAllocator<> 
    ] 
    and 
    [ 
     Encoding=rapidjson::UTF8<> 
    ] 

EDIT2:
Просьба игнорировать тот факт, что .c_str() называется на временной стоимости. Этот пример предназначен только для отображения ошибки компиляции. Фактический код использует строковую переменную.


EDIT3:
Альтернативный вариант кода:

string str("value") ; 
obj.AddMember("key", "value", doc.GetAllocator()) ; //compiles 
obj.AddMember("key", str, doc.GetAllocator()) ; // does not compile 
obj.AddMember("key", str.c_str(), doc.GetAllocator()) ; // does not compile 
+0

Включите ''? Кроме того, 'main()' всегда возвращает 'int'. –

+1

Строковый литерал имеет тип массива, а 'c_str()' возвращает указатель. Возможно иметь функциональные перегрузки, которые могут отличить друг от друга (но довольно недружелюбно заставить их вести себя по-разному). –

+0

Время жизни 'string (" value "). C_str()' очень ограничено. поэтому, если не будет выполнена копия 'const char *', у вас может быть висящий указатель. – Jarod42

ответ

2

Ознакомившись с документацией, с которой вы связались, похоже, что вы пытаетесь вызвать перегрузку AddMember с двумя StringRefType s (и Allocator). StringRefType является ЬурейеЕ для GenericStringRef<Ch>, который имеет две перегруженные конструкторы принимает один аргумент:

template<SizeType N> 
GenericStringRef(const CharType(&str)[N]) RAPIDJSON_NOEXCEPT; 

explicit GenericStringRef(const CharType *str); 

При передаче строковым, то тип const char[N], где N длина строки + 1 (для нулевой терминатор). Это может быть неявно преобразовано в GenericStringRef<Ch> с использованием первой перегрузки конструктора. Однако std::string::c_str() возвращает const char*, который не может быть преобразован неявно в GenericStringRef<Ch>, поскольку вторая перегрузка конструктора объявлена ​​explicit.

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

+0

Обратите внимание, что фактический аргумент, на который компилятор реагирует в цитируемой диагностике, является литералом или массивом, объявленным как таковой, вызовом 'char const [4]', а не c_str(). –

+0

@ Cheersandhth.-Alf Ошибка компилятора также относится к параметру 1. Следовательно, последнее предложение в моем ответе. –

+0

Итак, в основном метод 'AddMember' принимает строковые литералы, но не указатели' char'. – GetFree

5

Метод std::string::c_str() возвращает char const*. Тип строкового литерала - char const[N], где N - количество символов в строке (включая нулевой ограничитель). Соответственно, результат c_str() может не использовать в все места, где может использоваться строковый литерал!

Я был бы удивлен, если интерфейс, который вы пытаетесь вызвать, требует массив char. То есть, в вашем использовании это должно работать. Скорее всего, вам нужно включить <string>.

+0

Вы также не можете передавать c_str на то, что ожидает char * (не const), массив или нет. И вы также должны быть осторожны, чтобы функция, которую вы передаете ей, не хранит указатель где-то позже, который будет компилироваться, но в конечном итоге взорваться. –

+3

@JasonC: вы не можете передать строковый литерал к чему-то ожидающему 'char *', либо! Совместимость с обратной связью C для специального преобразования всегда была устаревшей и удалена с C++ 11. –

3

даже если этот код компилируется:

obj.AddMember("key2", string("value").c_str(), doc.GetAllocator()); 

Вы не можете гарантировать, что это безопасно.

Константа char *, возвращаемая std :: string :: c_str(), будет действительна до конца этого утверждения.

Если метод AddMember хранит копию самой строки, все хорошо и хорошо. Если он хранит указатель, то вы обречены. Вам необходимо знать внутреннюю работу AddMember, прежде чем вы сможете обосновать правильность вашего кода.

Я подозреваю, что авторы уже думали об этом и построили перегрузкам, которые требуют, чтобы вы либо отправить в std::string объекта (или эквивалент) или строковый литерал ссылки (template<std::size_t N> void AddMember(const char (&str)[N]))

Даже если это не то, что они имейте в виду, они могут искать, чтобы защитить вас от вас самих, в случае непреднамеренной отправки указателя недействительным.

Хотя это неудобство, эта ошибка времени компиляции указывает на возможную неисправную программу. Это дань уважения авторам библиотеки. Потому что ошибки времени компиляции в gazillion раз более полезны, чем ошибки времени выполнения.

+0

_ Вам нужно знать внутреннюю работу AddMember, прежде чем вы сможете обосновать правильность своего кода. - Ну, вам нужно внимательно прочитать документацию. Но быть осторожным с этой конструкцией - хороший совет, и я отдам дань уважения авторам библиотеки. –

2

Re

why is the return of .c_str() not equivalent to a string literal

Строка буквальная является нулем строки в массиве с размером известного во время компиляции.

c_str() создает указатель на (первый элемент) строки с нулевым завершением в массиве с размером, известным только во время выполнения.

Обычно выражение строкового литерала будет использоваться в контексте, где выражение распадается на на указатель на первый элемент, но в некоторых особых случаях он не распадается. Эти случаи включают

  • связывание с ссылкой на массив,

  • с помощью оператора sizeof и

  • образуя большую буквальный по времени компиляции конкатенации строковых литералов (просто писать их в порядке).

Я думаю, что это исчерпывающий список.


Сообщение об ошибке вы цитируете,

cannot convert parameter 1 from 'const char [4]' to 'rapidjson::GenericValue &

& hellip; не совпадающий свой кода, представленного

#include "rapidjson/document.h" 

int main(){ 
    using namespace rapidjson ; 
    using namespace std ; 

    Document doc ; 
    Value obj(kObjectType) ; 
    obj.AddMember("key1", "value", doc.GetAllocator()) ; //this compiles fine 
    obj.AddMember("key2", string("value").c_str(), doc.GetAllocator()) ; //this does not compile! 
} 

Нигде в этом коде есть три символа длинная строка буквальная.

Следовательно, утверждает, что “ это компилирует ” “ и это не компилирует ”, не очень надежные.

Вы

  • должен процитировали фактическое сообщение об ошибке и фактический код (по крайней мере один из них не то, что у вас, когда вы собрали), и

  • должен процитировали документацию функцию, которую вы вызываете.

Кроме того, обратите внимание, что фактический аргумент, что компилятор реагирует на цитируемый диагностики, является буквальным или массив объявлен как таковой, а не c_str() вызова.

+0

Возможно, VC++ не считает нулевое завершение? – GetFree

+0

@ GetFree: Он должен считать это. Это часть размера массива. –

+0

О, вы правы, я просто понял, что я добавил строку '' 1 "' и '" 2 "' в строку '' key '', когда я писал вопрос. Я исправлю это. – GetFree