개념의 양화는 개선인가, 나쁜 습관인가?

Aug 20 2020

개념에 람다를 넣은 다음 코드를 작성할 수 있습니다. 이것을 예로 들어 보겠습니다. 나는 이러한 개념에 대한 표준 개념을 선호하며 이것은이 예제의 목적만을위한 것임을 명심하십시오-godbolt

template<class T>
concept labdified_concept =
    requires {
            [](){                 
                T t, tt; // default constructible
                T ttt{t}; // copy constructible
                tt = t; //copy assignable
                tt = std::move(t); // move assignable
            };
        };

대신에:

template<class T>
concept normal_concept = 
    std::default_initializable<T> && std::movable<T> && std::copy_constructible<T>;

양육 화는 개선인가 나쁜 습관인가? 가독성 지점에서도.

답변

2 Barry Aug 21 2020 at 01:10

이것은 유효하지 않아야합니다. 평가되지 않은 컨텍스트로 허용되는 람다의 요점은 문에서 SFINAE를 갑자기 허용하는 것이 아닙니다.

[temp.deduct] / 9 에는이를 명확히하는 몇 가지 문구가 있습니다 .

람다 표현 기능 유형이나 템플릿 매개 변수에 나타나는 템플릿 인수 공제의 목적에 대한 즉각적인 환경의 일부로 간주되지 않습니다. [ 참고 : 의도는 임의의 명령문과 관련된 대체 실패를 처리하기 위해 구현을 요구하지 않는 것입니다. [ :

template <class T>
  auto f(T) -> decltype([]() { T::invalid; } ());
void f(...);
f(0);               // error: invalid expression not part of the immediate context

template <class T, std::size_t = sizeof([]() { T::invalid; })>
  void g(T);
void g(...);
g(0);               // error: invalid expression not part of the immediate context

template <class T>
  auto h(T) -> decltype([x = T::invalid]() { });
void h(...);
h(0);               // error: invalid expression not part of the immediate context

template <class T>
  auto i(T) -> decltype([]() -> typename T::invalid { });
void i(...);
i(0);               // error: invalid expression not part of the immediate context

template <class T>
  auto j(T t) -> decltype([](auto x) -> decltype(x.invalid) { } (t));   // #1
void j(...);                                                            // #2
j(0);               // deduction fails on #1, calls #2

끝 예 ] — 끝 참고 ]

요구 사항에 상응하는 것이 없습니다. gcc의 동작은 실제로 예상되는 것입니다.

template <typename T> concept C = requires { []{ T t; }; };
struct X { X(int); };
static_assert(!C<X>); // ill-formed

람다의 본문은 즉각적인 컨텍스트 외부에 있으므로 대체 실패가 아니므로 하드 오류입니다.

5 NicolBolas Aug 20 2020 at 21:58

이 메커니즘의 명백한 가독성 결함을 무시하면 실제로 작동 하지 않습니다 . 다음을 고려하세요:

template<labdified_concept T>
void foo(T t) {}

template<typename T>
void foo(T t) {}

개념의 규칙은 주어진 T것이 만족하지 않으면 labdified_concept다른 하나 foo가 대신 인스턴스화되어야한다고 말합니다 . 그러나 우리 SS가 그러한 템플릿을 제공하면 그것은 일어나지 않습니다 . 대신 labdified_concept<SS>인스턴스화 할 수 없기 때문에 하드 오류가 발생 합니다.

내 물건 requires표현은 특정 유형의 오류가 요구 사항을 충족하기 위해 실패로 간주 될 수 있도록 특수 처리가 있습니다. 그러나 그 처리는 람다의 본문에는 적용되지 않습니다. 여기에서 형식이 잘못된 코드는 형식이 잘못되어 인스턴스화하려고 할 때 컴파일 오류가 발생합니다.

그리고 그것이 작동하더라도 여전히 작동하지 않습니다. 개념은 개념의 포함에 대한 복잡한 규칙을 가지고 있으므로 다른 개념이 다른 개념보다 더 고도로 전문화 된 것으로 간주 될 수 있습니다. 이를 통해 다른 개념에 대한 오버로딩을 허용 하여 더 제한된 개념을 호출 할 수 있습니다. 예를 들어만을 필요로하는 개념은 default_initializable필요보다 더 일반적인 것입니다 default_initializablemoveable. 따라서 유형이 두 가지를 모두 충족하면 더 제한적이므로 후자가 사용됩니다.

그러나 이것은 concepts 에 대한 특별한 규칙 때문에 작동합니다 . 람다에서 요구 사항을 숨기면 이것이 작동하지 않습니다.