リストの初期化をコピーしますか?なぜこれがコンパイルされるのですか?


13

Microsoft Visual Studio Community 2019、V16.5.2を使用しています。リストの初期化をテストしたい

次のテストプログラムをご覧ください。

#include <string>

void foo(std::string str) {}

int main() {

    foo( {"str1", "str2"} );

    return 0;
}

これはエラーや警告なしでコンパイルされます。どうして?

実行時エラーが発生します。 Expression: Transposed pointer range

誰かがここで何が起こっているのか説明できますか?


編集。

コードを逆アセンブルしてデバッガで実行しました

    foo( {"str1", "str2"} );
00F739A8  sub         esp,1Ch  
00F739AB  mov         esi,esp  
00F739AD  mov         dword ptr [ebp-0C8h],esp  
00F739B3  lea         ecx,[ebp-0D1h]  
00F739B9  call        std::allocator<char>::allocator<char> (0F7136Bh)  
00F739BE  push        eax  
00F739BF  push        offset string "str2" (0F84DB8h)  
00F739C4  push        offset string "str1" (0F84E2Ch)  
00F739C9  mov         ecx,esi  
00F739CB  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> ><char const *,0> (0F71569h)  
00F739D0  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (0F71843h)  
00F739D5  add         esp,1Ch  

コンストラクタの最初の呼び出しでクラッシュしますか?


編集内容がわかりませんが、別の質問のようです。新しい質問を投稿する必要がありますか?
Mooing Duck

回答:


16

std::string開始/終了反復子のペアから文字列を構築するテンプレートコンストラクターがあります。C ++の文字列リテラルはconst char*sに発展します。そして、ポインタはイテレータです。したがって、リストの初期化では、開始/終了ペアコンストラクターが選択されました。

2つのポインターが実際には有効な範囲を作成しないため、ランタイムエラーが発生しました。有効範囲は、コンパイル時に決定できません(通常)。


わかります。範囲コンストラクター。コードを逆アセンブルしてデバッグしました。コンストラクタの最初の呼び出しでクラッシュします。わかりません<char const *,0>。誰かがこれを説明できますか?
Armin Montigny

THatはtemplate< InputIt > (InputIt first, InputIt last,...)、テンプレートパラメーターiterconst char*... であるコンストラクターを呼び出していることを意味します。どういうわけか、実装に2番目の整数パラメーターがあるようです。
Mooing Duck

@ArminMontigny:何を説明しますか?分解は本質的に無関係です。コードは構文的に有効であると述べられていますが、有効な範囲の反復子を渡していないため、未定義の動作が発生します。コードが機能しない理由を理解するために、逆アセンブリを理解する必要はありません。
Nicol Bolas

8

std::string 次の形式のコンストラクタオーバーロードがあります。

template< class InputIt >
basic_string( InputIt first, InputIt last,
              const Allocator& alloc = Allocator() );

そしてこれは、呼び出される"str1""str2"崩壊するconst char*のとconst char*許容イテレータ型です。

関数に渡した「イテレータ範囲」が無効であるため、クラッシュします。


ありがとう、わかりました。+1。編集をご覧ください。
Armin Montigny

7

これは、std :: string(6.)の反復子を持つコンストラクターを使用します。

template< class InputIt >
constexpr basic_string( InputIt first, InputIt last,
                        const Allocator& alloc = Allocator() );

[ InputIt= const char*]付き。

次に、範囲{"str1", "str2"}が無効であるため、UB があります。


ありがとう、わかりました。+1。編集をご覧ください。
Armin Montigny
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.