명시 적 (bool)의 유스 케이스는 무엇입니까


24

C ++ 20은 생성시 명시 적 으로 여부를 컴파일 타임에 조건부로 선택하는 명시 적 (bool) 을 도입했습니다 .

아래는 내가 발견 한 예입니다 여기가 .

struct foo {

  // Specify non-integral types (strings, floats, etc.) require explicit construction.

  template <typename T>

  explicit(!std::is_integral_v<T>) foo(T) {}

};

foo a = 123; // OK

foo b = "123"; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true)

foo c {"123"}; // OK

누구든지 explicit (bool)사용 이외의 다른 유스 케이스를 말해 줄 수 있습니까 std::is_integral?


1
한 가지 예는 tuple이 기능 을 사용 하는 것과 같은 조건부 명시 적 생성자를 구현하는 것이 훨씬 쉬워진다는 것 입니다.
Praetorian

1
정답은 아니지만 논문을 소개 한 동기를 살펴볼 수도 있습니다 : wg21.link/p0892
N. Shead

예 : 개념과 함께 필요한 수의 기본 클래스를 줄여 조건부로 제공된 조건부 명시 적 복사 생성자를 3에서 0으로 구현합니다.
LF

답변:


21

동기 부여 자체는 논문 에서 볼 수있다 .

생성자를 조건부로 명시 적으로 만들어야합니다. 즉, 당신은 원합니다 :

pair<string, string> safe() {
    return {"meow", "purr"}; // ok
}

pair<vector<int>, vector<int>> unsafe() {
    return {11, 22}; // error
}

전자는 괜찮습니다. 그 생성자는 암시 적입니다. 그러나 후자는 좋지 않을 것 explicit입니다. C ++ 17 (또는 개념이있는 C ++ 20)을 사용하면이 작업을 수행 할 수있는 유일한 방법은 두 가지 생성자를 작성하는 것입니다 explicit.

template <typename T1, typename T2>
struct pair {
    template <typename U1=T1, typename U2=T2,
        std::enable_if_t<
            std::is_constructible_v<T1, U1> &&
            std::is_constructible_v<T2, U2> &&
            std::is_convertible_v<U1, T1> &&
            std::is_convertible_v<U2, T2>
        , int> = 0>
    constexpr pair(U1&&, U2&& );

    template <typename U1=T1, typename U2=T2,
        std::enable_if_t<
            std::is_constructible_v<T1, U1> &&
            std::is_constructible_v<T2, U2> &&
            !(std::is_convertible_v<U1, T1> &&
              std::is_convertible_v<U2, T2>)
        , int> = 0>
    explicit constexpr pair(U1&&, U2&& );    
};  

이것들은 거의 완전히 복제되어 있으며이 생성자의 정의는 동일합니다.

을 사용하면 explicit(bool)단일 생성자를 작성할 수 있습니다. 조건부로 명시적인 구성 부분이 explicit-specifier로 지역화되어 있습니다 .

template <typename T1, typename T2>
struct pair {
    template <typename U1=T1, typename U2=T2,
        std::enable_if_t<
            std::is_constructible_v<T1, U1> &&
            std::is_constructible_v<T2, U2>
        , int> = 0>
    explicit(!std::is_convertible_v<U1, T1> ||
        !std::is_convertible_v<U2, T2>)
    constexpr pair(U1&&, U2&& );   
};

이것은 의도와 더 잘 일치하고 작성하는 코드가 훨씬 적으며 과부하 해결 중에 컴파일러가 수행 할 작업이 적습니다 (선택해야 할 생성자가 적기 때문에).


1
C ++ 20은 enable_if_t개념을 사용하여 부품을 더 예쁘고 간단한 구속 조건 으로 변경할 수있는 기능도 제공합니다 . 그러나 그것은이 질문의 핵심입니다.
aschepler

2

내가 볼 수있는 또 다른 사용법은 가변 템플릿을 사용하는 것입니다.

일반적으로 기본적으로 explicit하나의 인수 만있는 생성자 를 갖는 것이 좋습니다 (변환이 필요한 경우 제외).

그래서

struct Foo
{
    template <typename ... Ts>
    explicit(sizeof...(Ts) == 1) Foo(Ts&&...);

    // ...
};

0

explicit입력이 std::string_view호출 후 새로운 객체가 유지할 뷰와 같은 유형 (원시 포인터) 일 때 조건부 로 요구되는 유스 케이스를 볼 수 있습니다 (참조가 아닌 뷰를 복사하는 것만으로 의존합니다) 본 객체의 수명) 또는 값과 유사한 유형일 수 있습니다 (외부 수명 종속성없이 사본의 소유권을 가짐).

이와 같은 상황에서 호출자는 조회 된 객체를 살아있는 상태로 유지해야합니다 (수신자가 원래 객체가 아닌 뷰를 소유 함). 그것이 보는 객체보다 오래 지속됩니다. 반대로 값 형식의 경우 새 개체는 자체 복사본을 받게되므로 복사 비용이 많이 들지만 암시 적 변환이 발생해도 코드가 잘못 되지는 않습니다 .

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