C ++ 20 이전에 std :: swap 표시된 constexpr이 아닌 이유는 무엇입니까?


14

C ++ 20, std::swapa가됩니다 constexpr기능.

나는 표준 라이브러리가 사물을 표시하는 데 실제로 언어보다 뒤떨어져 있음을 알고 constexpr있지만 2017 년 <algorithm>에는 다른 것들과 마찬가지로 거의 constexpr이었습니다. 그러나 std::swap그렇지 않았습니다. 나는 그 표시를 막는 이상한 언어 결함이 있다는 것을 모호하게 기억하지만 세부 사항은 잊어 버립니다.

누군가 간결하고 명확하게 설명 할 수 있습니까?

동기 부여 : C ++ 11 / C ++ 14 코드에서 std::swap()유사한 함수 를 표시하는 것이 왜 나쁜 아이디어인지 이해해야 constexpr합니다.

답변:


11

이상한 언어 문제는 CWG 1581입니다 .

15 절 [특별]은 특수 멤버 함수가 사용시에만 암시 적으로 정의된다는 것을 완벽하게 명확하게 알 수 있습니다. 이것은 평가되지 않은 컨텍스트에서 상수 표현식에 대한 문제를 만듭니다.

struct duration {
  constexpr duration() {}
  constexpr operator int() const { return 0; }
};

// duration d = duration(); // #1
int n = sizeof(short{duration(duration())});

여기서 문제는 우리 constexpr duration::duration(duration&&)가이 프로그램에서 암시 적으로 정의 할 수 없으므로 초기화 목록의 표현식이 상수 표현식이 아닙니다 (정의되지 않은 constexpr 함수를 호출하기 때문에) 따라서 프로그램이 잘못 구성되었습니다.

1 행의 주석을 해제하면 이동 생성자가 암시 적으로 정의되고 프로그램이 유효합니다. 멀리서이 무시 무시한 행동은 매우 불행합니다. 이 시점에서 구현이 다양합니다.

나머지 문제 설명을 읽을 수 있습니다.

이 문제에 대한 해결책 은 2017 년 앨버 커키 P0859 (C ++ 17 출하 후)에 채택되었습니다 . 이 문제는 C ++ 20 모두에 대해 ( P0879constexpr std::swap 에서 해결됨 ) 및 constexpr std::invoke( PWG65 에서 CWG1581 예제가 있는 P1065 에서 해결됨 ) 둘 다 가질 수있는 차단제였습니다 .


내 생각에 가장 이해하기 쉬운 예는 P1065에서 지적한 LLVM 버그 보고서의 코드입니다.

template<typename T>
int f(T x)
{
    return x.get();
}

template<typename T>
constexpr int g(T x)
{
    return x.get();
}

int main() {

  // O.K. The body of `f' is not required.
  decltype(f(0)) a;

  // Seems to instantiate the body of `g'
  // and results in an error.
  decltype(g(0)) b;

  return 0;
}

CWG1581은 constexpr 멤버 함수가 정의 된 시점 에 관한 것이며 해상도는 사용시에만 정의되도록합니다. P0859 이후에는 위의 형식이 양호합니다 (유형은 b입니다 int).

이후 std::swapstd::invoke모두하는 멤버 함수에 대한 (이전의 이동 건축 / 할당하고 호출 연산자 / 후자의 대리 전화), 그들은 모두이 문제의 해결에 의존 확인에 의존해야합니다.


그렇다면 왜 CWG-1581이 스왑 기능을 constexpr로 표시하는 것을 막거나 바람직하지 않게합니까?
einpoklum

3
@einpoklum 교환은 std::is_move_constructible_v<T> && std::is_move_assignable_v<T>입니다 true. 특수 멤버 함수가 아직 생성되지 않은 경우에는 발생할 수 없습니다.
NathanOliver

@NathanOliver : 내 대답에 이것을 추가했습니다.
einpoklum

5

이유

(@NathanOliver로 인해)

constexpr스왑 기능 을 허용하려면 이 기능에 대한 템플릿을 인스턴스화하기 전에 스왑 된 유형이 이동 구성 가능하고 이동 지정 가능한지 확인해야합니다. 불행하게도, 결함에만 C ++ 20에서 해결 언어로 인해, 당신은 할 수없는 관련 멤버 함수가 멀리 컴파일러에 관한 한, 아직 정의되지 않았을 수 있기 때문에, 그 확인합니다.

연대기

  • 2016 : 안토니 Polukhin submitts 제안 P0202 의 모든 표시하는 <algorithm>등의 기능을 constexpr.
  • 표준위원회의 핵심 실무 그룹은 결함 CWG-1581에 대해 논의 합니다. 이 문제는 문제가 만들어 constexpr std::swap()constexpr std::invoke()위의 설명을 참조 -.
  • 2017 : 안토니는 개정 그의 몇 번 제외 할 제안 std::swap과 다른 구조를, 이것은 C ++ (17)에 허용됩니다.
  • 2017 : CWG-1581 문제에 대한 해결책은 P0859 로 제출 되어 2017 년 표준위원회 (C ++ 17 출하 후)에서 승인되었습니다.
  • 2017 년 말 : Antony는 CWG-1581 결의 후 constexpr 을 만들기 위해 보완 제안 P0879를 제출합니다 std::swap().
  • 2018 : 보완 제안이 C ++ 20에 허용됩니다 (?). 배리가 지적했듯이 constexpr std::invoke()수정도 마찬가지입니다 .

특정 사례

당신이 사용할 수있는 constexpr당신이 경우에 교환을 하지 않는 이동-constructibility 및 이동 - 양도성 확인, 오히려 특정의 것을 보장 유형의 다른 기능을 확인합니다. 예를 들어 기본 유형 만 있고 클래스 나 구조체는 없습니다. 또는 이론적으로 검사를 포기하고 발생할 수있는 컴파일 오류와 컴파일러 간 비정상적인 동작 전환을 처리 할 수 ​​있습니다. 어쨌든 std::swap()그런 종류의 물건으로 바꾸지 마십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.