2017-12-26 22 views
16

C++로 전환하고 사용자 정의 std::optional 솔루션을 표준 I/F로 바꾸면 clang 5의 예기치 않은 동작이 감지되었습니다. 어떤 이유로 인해 std::is_constructible 특성 클래스의 특성이 잘못 평가되어 emplace()이 사용 중지되었습니다.clang 5 : std :: optional instantiation screws std :: is_constructible 특성 매개 변수 유형의 특성

#include <optional> 

/// Precondition #1: T must be a nested struct 
struct Foo 
{ 
    struct Victim 
    { 
     /// Precondition #2: T must have an aggregate-initializer 
     /// for one of its members 
     std::size_t value{0}; 
    }; 

    /// Precondition #3: std::optional<T> must be instantiated in this scope 
    std::optional<Victim> victim; 

    bool foo() 
    { 
     std::optional<Victim> foo; 

     // An error 
     foo.emplace(); 
     /// Assertion is failed 
     static_assert(std::is_constructible<Victim>::value); 
    } 
}; 

라이브 예를 전제 조건의 변경


godbolt.org과 예상대로 컴파일 : 그것은 재현하기 전에

일부 특정 전제 조건

을 만족해야합니다. 표준에서 불명확 한 불일치가 발생하여 준수하는 동안 clang이이 코드를 거부하게됩니까? 보조 노트로

: GCC 7.1 및 GCC 7.2은 위의 코드와 아무 문제가 있습니다.


버그 보고서에 : bugs.llvm.org

+0

컴파일러 버그 일 수 있습니다. –

+0

@CrisLuengo, 표준보다 수정하기가 쉽기 때문에 가능합니다. – GreenScape

+1

핵심은 귀하가 언어 변호사의 질문입니다. 그것은 그렇게 대답해야합니다. – StoryTeller

답변

7

이것은 컴파일러의 버그처럼 보인다. [class]

에서 클래스는 클래스 지정자의 폐쇄 }에 완전히 정의 된 오브젝트 타입 (또는 완전한 형태)를 고려한다. Victim이 맥락에서 다른 유형과는 전혀 다른 제작하지 std::optional<Victim>에서 완전한 의미

. [meta]

에서

다음과 같은 변수 정의가 일부 잘 형성 될 경우에만 경우 템플릿 특수화 is_­constructible<T, Args...>에 대한 술어 조건이 만족되어야한다 변수 t 발명 : T t(declval<Args>()...);

어떤 유형의 인수가 Args...t을 직접 초기화하는지 또는 sizeof...(Args) == 0 인 경우을 값 초기화합니다. 0.

이 경우 value-initializing t is to default-initializet이 유효하므로 std::is_constructible_v<Victim>이어야합니다.

모든 말로 컴파일러는 struggling a lot입니다.

+1

동일한 단락에서 다음과 같이 설명합니다. "클래스 멤버 지정 내에서 클래스는 기본 멤버 초기화 프로그램 (중첩 클래스의 클래스 포함) 내에서 완전한 것으로 간주됩니다." 이는 컴파일러가 닫는'}'을 볼 때까지 (그리고 중첩 된 클래스의 경우, 닫는 클래스의 닫는'} '을 볼 때까지 기본 멤버 초기화 프로그램을 처리 할 수 ​​없다는 것을 의미합니다. – cpplearner

+2

@cpplearner 나는 (둘러싼) 클래스가 자체 몸 안에서 불완전하다는 것을 믿는다. 중첩 된 클래스가 불완전하지는 않다. –

+1

내 요점은 기본 멤버 이니셜 라이저가 여전히 토큰 스프입니다.그것들은 둘러싸는 클래스 내에서 파싱되지 않습니다.) 그리고 집계를 초기화하는 것은이 기본 멤버 초기화를 참조 할 필요가 있습니다. 이것은 SFINAE 컨텍스트 외부에서보다 분명합니다.'struct Outer {struct Inner {int x = 4; }; decltype (내부()) a; };' – cpplearner

3

해당 인용문을 찾아보십시오. 이 문제의 핵심은 std::is_constructibleVictim을 어떻게 처리해야하는지입니다. 가장 확실한 권한은 C++ 17 (n4659)입니다.먼저 [meta.unary.prop/8] :

T t(declval<Args>()...); 

[: is_­constructible<T, Args...>이 변수 정의 다음 일부 잘 형성 될 경우에만 경우 만족한다 템플릿 전문화에 대한 술어 조건이 변수 t를 발명

참고 : 이 토큰은 절대로 함수 선언으로 해석되지 않습니다. - end note] 액세스 확인은 T와 임의의 Arg와는 관련이없는 문맥 에서와 같이 수행됩니다. 변수 초기화의 즉시 컨텍스트의 유효성 만 고려됩니다.

내가 강조 표시 노트 (메모되는 계정) 규범은 아니지만 [temp.variadic]/7과 일치 : N이 0이면

이 ..., 확장의 인스턴스가 을 생산 빈 목록. 그러한 인스턴스화는 이 목록을 완전히 생략하는 것이 부적절하거나 이 문법에 모호한 결과를 초래하는 경우에도 구문을 포함하는 구문의 구문을 변경하지 않습니다.

그래서 is_­constructible의 목적을 위해,이 T t();t 변수 선언합니다.

그 이니셜 빈} 인 목적, 즉,() 값으로 초기화한다 : [dcl.init/11]가 많이 말한다 때문에 초기 값이 초기화된다.

즉, Victim을 값 초기화 할 수 있는지 확인하는 특성입니다. 어느 것이 좋습니다. 이것은 집합체이지만 암시 적으로 디폴트 된 디폴트 c'tor는 여전히 (값 초기화를 지원하기 위해) 컴파일러에 의해 정의됩니다.

짧은 이야기. Clang에게는 버그가 있습니다. 신고해야합니다.