Skopiować inicjalizację listy? Dlaczego to się kompiluje?

13

Korzystam z Microsoft Visual Studio Community 2019, V16.5.2. Chcę przetestować inicjalizację listy

Zobacz następujący program testowy:

#include <string>

void foo(std::string str) {}

int main() {

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

    return 0;
}

Kompiluje się bez błędów i ostrzeżeń. Dlaczego?

Daje błąd środowiska wykonawczego: Expression: Transposed pointer range

Czy ktoś może wyjaśnić, co się tutaj dzieje?


Edytować.

Zdemontowałem kod i uruchomiłem go w debuggerze

    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  

Awarie przy pierwszym wywołaniu do konstruktora?

Armin Montigny
źródło
Nie rozumiem twojej edycji, ale wygląda na to, że może to inne pytanie, więc może musisz opublikować nowe pytanie?
Kaczka Mooing

Odpowiedzi:

16

std::stringma konstruktor szablonów, który buduje ciąg z pary iteratora początku / końca. Literały łańcuchowe w C ++ przechodzą do const char*s. Wskaźniki są iteratorami. Dlatego podczas inicjowania listy wybrano konstruktor pary początkowej / końcowej.

Wystąpił błąd w czasie wykonywania, ponieważ dwa wskaźniki w rzeczywistości nie tworzą prawidłowego zakresu, którego nie można ustalić w czasie kompilacji (ogólnie).

Nicol Bolas
źródło
Rozumiem. Konstruktor zasięgu. Zdemontowałem i zdebugowałem kod. Awaria podczas pierwszego wywołania konstruktora. Nie rozumiem <char const *,0>. Czy ktoś może to wyjaśnić?
Armin Montigny
Oznacza to, że wywołuje template< InputIt > (InputIt first, InputIt last,...)konstruktor, w którym iterznajduje się parametr szablonu const char*.... i najwyraźniej twoja implementacja ma z jakiegoś powodu drugi parametr liczby całkowitej?
Kaczka Mooing
@ArminMontigny: Wyjaśnij co? Demontaż jest zasadniczo nieistotny. Twój kod jest poprawny pod względem składniowym, ale daje niezdefiniowane zachowanie z powodu nieprzekazania prawidłowego zakresu iteratorów. Nie musisz rozumieć dezasemblacji, aby zrozumieć, dlaczego twój kod nie działa.
Nicol Bolas,
8

std::string ma przeciążenie konstruktora w postaci

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

i to się nazywa bo "str1"i "str2"rozpada się na const char*i const char*jest akceptowalnym typem iteratora.

Występuje awaria, ponieważ „zakres iteratora” przekazany do funkcji jest nieprawidłowy.

NathanOliver
źródło
Dzięki, zrozumiałe. +1. Zobacz edycję.
Armin Montigny
7

Które używają konstruktora z iteratorami std :: string (6.).

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

Z [ InputIt= const char*].

Następnie masz UB, ponieważ zakres {"str1", "str2"}jest nieprawidłowy.

Jarod42
źródło
Dzięki, zrozumiałe. +1. Zobacz edycję.
Armin Montigny