Inicialização da lista de cópias? Por que isso compila?

13

Estou usando a Comunidade do Microsoft Visual Studio 2019, V16.5.2. Quero testar a inicialização da lista

Por favor, veja o seguinte programa de teste:

#include <string>

void foo(std::string str) {}

int main() {

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

    return 0;
}

Isso compila sem erro e aviso. Por quê?

Dá um erro de tempo de execução: Expression: Transposed pointer range

Alguém pode explicar o que está acontecendo aqui?


Editar.

Desmontei o código e o executei no depurador

    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  

Falha na primeira chamada para o construtor?

Armin Montigny
fonte
Não entendo sua edição, mas parece que talvez seja uma pergunta diferente; talvez você precise postar uma nova pergunta para ela?
Mooing Duck

Respostas:

16

std::stringpossui um construtor de modelo que cria uma cadeia de caracteres a partir de um par de iterador de início / fim. Literais de string em C ++ são reduzidos para const char*s. E ponteiros são iteradores. Portanto, a inicialização da lista escolheu o construtor de pares de início / fim.

Você obteve um erro de tempo de execução porque os dois ponteiros não criam realmente um intervalo válido, que não pode ser determinado em tempo de compilação (geralmente).

Nicol Bolas
fonte
Compreendo. O construtor de intervalo. Desmontei e depurei o código. Falha na primeira chamada ao construtor. Eu não entendo <char const *,0>. Alguém pode explicar isso?
Armin Montigny
Isso significa que ele está chamando o template< InputIt > (InputIt first, InputIt last,...)construtor onde o parâmetro do modelo iteré const char*.... e, aparentemente, sua implementação tem um segundo parâmetro inteiro por algum motivo?
Mooing Duck
@ArminMontigny: Explique o que? A desmontagem é essencialmente irrelevante. Seu código é declarado sintaticamente válido, mas oferece um comportamento indefinido devido à não aprovação de um intervalo válido de iteradores. Você não precisa entender a desmontagem para entender por que seu código não é funcional.
Nicol Bolas 03/04
8

std::string tem uma sobrecarga de construtor na forma de

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

e isso é chamado porque "str1"e "str2"decadência para const char*'s e const char*é um tipo de iterador aceitável.

Você sofre uma falha porque o "intervalo do iterador" que você passou para a função é inválido.

NathanOliver
fonte
Obrigado, entendido. +1. Por favor, veja a edição.
Armin Montigny
7

Que usam o construtor com iteradores de std :: string (6.).

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

Com [ InputIt= const char*].

Então você tem UB, pois o intervalo {"str1", "str2"}é inválido.

Jarod42
fonte
Obrigado, entendido. +1. Por favor, veja a edição.
Armin Montigny