2016-12-24 2 views
2

このコードの最後の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! 
} 

私の推測は間違っています。 1行はコンパイルされ、もう1行はコンパイルされません。

hereのように、AddMemberメソッドにはいくつかのバリエーションがありますが、それ以外の理由は... .c_str()が文字列リテラルに相当しないのはなぜですか?

私は文字列リテラルが受け入れられたところで、string::c_str()を渡すことができました。

PS:
#include <string>の欠如が問題ではありません:私はVC++ 2010

EDITしてコンパイルしています。 .c_str()は時間的価値に呼ばれているという事実を無視してください

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:それはすでにdocument.h

によってこのエラーであるが含まれています。この例はコンパイルエラーを表示するためのものです。実際のコードは文字列変数を使用します。


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のオーバーロードを2つのStringRefType(とAllocator)と呼んでいるようです。 StringRefTypeは、単一の引数を取る2つのオーバーロードされたコンストラクタを持っているGenericStringRef<Ch>のtypedef、次のとおりです。

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

explicit GenericStringRef(const CharType *str); 

あなたは文字列リテラルを渡すと、型がNがヌル文字列+ 1(の長さであるconst char[N]、ありますターミネーター)。これは、最初のコンストラクタのオーバーロードを使用して暗黙的にGenericStringRef<Ch>に変換できます。ただし、std::string::c_str()const char*を返します。第2のコンストラクタのオーバーロードがexplicitと宣言されているため、暗黙的にGenericStringRef<Ch>に変換することはできません。

コンパイラから受け取ったエラーメッセージは、より近い一致であるAddMemberの別のオーバーロードを選択することによって発生します。

+0

コンパイラが引用された診断で反応する実際の引数は、リテラルまたはそのように宣言された配列、c_str()呼び出しではなく 'char const [4]'であることに注意してください。 –

+0

@ Cheersandhth.-Alfコンパイラエラーもパラメータ1を参照しています。したがって、私の答えの最後の文。 –

+0

基本的に、 'AddMember'メソッドは文字列リテラルを受け入れますが、' char'ポインタは受け付けません。 – GetFree

5

std::string::c_str()方法はchar const*を返します。文字列リテラルのタイプはchar const[N]です。ここで、Nは、文字列内の文字数(NULL終端文字を含む)です。これに対応して、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()); 

あなたはそれが安全であることを保証することはできません。

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

+0

_コードの正当性を判断する前に、AddMemberの内部の仕組みを知っておく必要があります._ - 文書を慎重に読む必要があります。しかし、このコンストラクトに注意することは良いアドバイスであり、私は図書館の著者にあなたの敬意を表します。 –

2

リテラル文字列は、コンパイル時に知られているサイズと、アレイ内のゼロで終了する文字列です。

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

は3つの文字長い文字列リテラルがあると一致していません。

したがって、“コンパイル”と“これはコンパイルされません”は、信頼性が低いです。

あなたは

  • は、実際のエラーメッセージと実際のコードを引用している必要があります(それらの少なくとも1つは、あなたがコンパイル時に持っていたものではありません)、そして

  • はの文書を引用している必要があります呼び出している関数。

はまた、実際の引数は、コンパイラことを引用し、診断には、リテラルまたは、このような、ないc_str()コールとして宣言された配列をあるに反応することに注意してください。

+0

VC++はヌル終了を数えていませんか? – GetFree

+0

@GetFree:それはそれを数えなければなりません。これは配列サイズの一部です。 –

+0

ああ、あなたが正しいです、私は質問を書いている間に "キー"文字列に '' 1 ''と' '2" 'を追加したことに気付きました。私はそれを修正します。 – GetFree

関連する問題