Initialisierung der Kopierliste? Warum wird dies kompiliert?

13

Ich verwende Microsoft Visual Studio Community 2019, V16.5.2. Ich möchte die Listeninitialisierung testen

Bitte beachten Sie das folgende Testprogramm:

#include <string>

void foo(std::string str) {}

int main() {

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

    return 0;
}

Dies wird ohne Fehler und Warnung kompiliert. Warum?

Es gibt einen Laufzeitfehler: Expression: Transposed pointer range

Kann jemand bitte erklären, was hier passiert?


Bearbeiten.

Ich habe den Code zerlegt und im Debugger ausgeführt

    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  

Es stürzt beim ersten Aufruf des Konstruktors ab?

Armin Montigny
quelle
Ich verstehe Ihre Bearbeitung nicht, aber es sieht nach einer anderen Frage aus. Vielleicht müssen Sie eine neue Frage dafür stellen?
Mooing Duck

Antworten:

16

std::stringhat einen Vorlagenkonstruktor, der eine Zeichenfolge aus einem Anfang / Ende-Iteratorpaar erstellt. String-Literale in C ++ gehen aufconst char* s . Und Zeiger sind Iteratoren. Daher hat die Listeninitialisierung den Anfang / Ende-Paarkonstruktor ausgewählt.

Sie haben einen Laufzeitfehler erhalten, weil die beiden Zeiger keinen gültigen Bereich erstellen, der (im Allgemeinen) zur Kompilierungszeit nicht ermittelt werden kann.

Nicol Bolas
quelle
Ich verstehe. Der Bereichskonstruktor. Ich habe den Code zerlegt und debuggt. Es stürzt beim ersten Aufruf des Konstruktors ab. Das verstehe ich nicht <char const *,0>. Kann das bitte jemand erklären?
Armin Montigny
Das heißt, es ruft den template< InputIt > (InputIt first, InputIt last,...)Konstruktor auf, in dem sich der Vorlagenparameter iterbefindet const char*... und anscheinend hat Ihre Implementierung aus irgendeinem Grund einen zweiten ganzzahligen Parameter?
Mooing Duck
@ArminMontigny: Was erklären? Die Demontage ist im Wesentlichen irrelevant. Ihr Code wird als syntaktisch gültig angegeben, weist jedoch ein undefiniertes Verhalten auf, da kein gültiger Bereich von Iteratoren übergeben wird. Sie müssen die Demontage nicht verstehen, um zu verstehen, warum Ihr Code nicht funktioniert.
Nicol Bolas
8

std::string hat eine Konstruktorüberladung in Form von

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

und dies wird aufgerufen, weil "str1"und "str2"zerfällt zu const char*'s und const char*ist ein akzeptabler Iteratortyp.

Sie erhalten einen Absturz, weil der "Iteratorbereich", den Sie an die Funktion übergeben haben, ungültig ist.

NathanOliver
quelle
Danke, verstanden. +1. Bitte beachten Sie die Bearbeitung.
Armin Montigny
7

Diese verwenden den Konstruktor mit Iteratoren von std :: string (6.).

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

Mit [ InputIt= const char*].

Dann haben Sie UB, da der Bereich {"str1", "str2"}ungültig ist.

Jarod42
quelle
Danke, verstanden. +1. Bitte beachten Sie die Bearbeitung.
Armin Montigny