복사 목록 초기화? 이것이 왜 컴파일됩니까?


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 로 축소됩니다 . 그리고 포인터는 반복자입니다. 따라서 목록 초기화는 시작 / 종료 쌍 생성자를 선택했습니다.

두 포인터가 실제로 유효한 범위를 만들지 않기 때문에 런타임 오류가 발생했습니다. 컴파일 타임에 (일반적으로) 결정할 수 없습니다.


이해 했어요. 범위 생성자 코드를 분해하고 디버깅했습니다. 생성자에 대한 첫 번째 호출에서 충돌이 발생합니다. 이해가 안 돼요 <char const *,0>. 누군가 이것을 설명해 주시겠습니까?
Armin Montigny

THat은 template< InputIt > (InputIt first, InputIt last,...)템플릿 매개 변수 iterconst char*.... 인 곳 에서 생성자를 호출한다는 것을 의미 하며 분명히 어떤 이유로 구현에 두 번째 정수 매개 변수가 있습니까?
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.