Search code examples
c++castingc-stringsstring-literalsrapidjson

Can std::string::c_str() be used whenever a string literal is expected?


I would guess that the last two lines in this code should compile.

#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!
}

My guess would be wrong, though. One line compiles and the other does not.

The AddMember method has several variants as documented here, but beyond that... why is the return of .c_str() not equivalent to a string literal?

My understanding was that where ever a string literal was accepted, you could pass string::c_str() and it should work.

PS: I'm compiling with VC++ 2010.

EDIT:
The lack of #include <string> is not the problem. It's already included by document.h

This is the error:

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:
Please ignore the fact that .c_str() is called on a temporal value. This example is just meant to show the compile error. The actual code uses a string variable.


EDIT3:
Alternate version of the code:

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

Solution

  • Looking at the documentation you linked to, it seems like you are trying to call the overload of AddMember taking two StringRefTypes (and an Allocator). StringRefType is a typedef for GenericStringRef<Ch>, which has two overloaded constructors taking a single argument:

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

    When you pass a string literal, the type is const char[N], where N is the length of the string + 1 (for the null terminator). This can be implicitly converted to a GenericStringRef<Ch> using the first constructor overload. However, std::string::c_str() returns a const char*, which cannot be converted implicitly to a GenericStringRef<Ch>, because the second constructor overload is declared explicit.

    The error message you get from the compiler is caused by it choosing another overload of AddMember which is a closer match.