只要需要字符串文字,就可以使用 std::string::c_str() 吗?

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

我猜想这段代码的最后两行应该可以编译。

#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() 的 return 不等同于字符串文字?

我的理解是,只要字符串文字被接受,您就可以传递 string::c_str() 它应该可以工作。

PS:我正在使用 VC++ 2010 进行编译。

编辑:
缺少 #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<>
    ]

编辑2:
请忽略 .c_str() 是在时间值上调用的事实。这个例子只是为了显示编译错误。实际代码使用字符串变量。


编辑3:
代码的替代版本:

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

std::string::c_str()方法returns一个char const*。字符串文字的类型是 char const[N],其中 N 是字符串中的字符数(包括空终止符)。相应的,c_str()的结果可以不能用在所有个可以使用字符串字面量的地方!

不过,如果您尝试调用的接口需要一个 char 数组,我会感到很惊讶。也就是说,在您的使用中它应该可以工作。您更有可能需要包含 <string>.

即使编译了这段代码:

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

您不能保证它是安全的。

std::string::c_str() 返回的 const char* 将在本语句结束前有效。

如果 AddMember 方法存储字符串本身的副本,一切都很好。如果它存储一个指针,那么你就完蛋了。在推断代码的正确性之前,您需要了解 AddMember 的内部工作原理。

我怀疑作者已经想到了这一点并构建了重载,要求您发送 std::string 对象(或等效对象)或字符串文字引用 (template<std::size_t N> void AddMember(const char (&str)[N]))

即使这不是他们想要的,他们也可能希望保护您免受自己的伤害,以防您无意中发送无效指针。

虽然看起来很不方便,但这个编译时错误表明程序可能有问题。这是对图书馆作者的致敬。因为编译时错误比运行时错误有用很多倍。

回复

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 &

与您提供的代码不匹配

#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() 调用。

查看您链接到的文档,您似乎正试图调用 AddMember 的重载,使用两个 StringRefType(和一个 Allocator)。 StringRefTypeGenericStringRef<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() returns a const char* 不能隐式转换为 GenericStringRef<Ch>,因为第二个构造函数重载声明为 explicit.

您从编译器收到的错误消息是由于它选择了另一个更接近匹配的 AddMember 重载。