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?


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).


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.


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.


Dzięki, zrozumiałe. +1. Zobacz edycję.
— Armin Montigny
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.