一个概念的 lambdification 是改进还是不好的做法?

Is lambdification of a concept an improvement or bad practice?

看来可以把lambda放在概念里面,然后在里面写代码。让我们以此为例。我更喜欢此类概念的标准概念,请记住,这仅用于此示例的目的 - 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>;

lambdification 是一种改进还是不良做法?从可读性的角度来看也是如此。

忽略此机制中明显的可读性缺陷,它实际上不起作用。考虑以下因素:

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

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

概念规则告诉我们,如果给定的T不满足labdified_concept,那么应该实例化另一个foo。但是,如果我们向这样的模板提供 SS 则不会发生这种情况。相反,我们得到一个硬错误,因为 labdified_concept<SS> 无法实例化。

requires表达式中的内容有特殊处理,允许将某些类型的错误视为不符合要求。但是这种处理不适用于 lambda 的主体。在那里,ill-formed 代码是 ill-formed,因此在尝试实例化它时会出现编译错误。

而且即使它确实起作用了,它仍然不起作用。概念具有包含概念的复杂规则,这允许不同的概念被认为比其他概念更高度专业化。这允许 ,它可以调用更受约束的概念。例如,只需要 default_initializable 的概念比需要 default_initializablemoveable 的概念更通用。因此,如果一个类型满足两者,则后者将被采用,因为它更受约束。

但这只适用于 concepts 的特殊规则。在 lambda 中隐藏需求将不允许此工作。

这应该是无效的。允许 lambda 进入未计算上下文的目的并不是突然允许 SFINAE on statements。

我们在 [temp.deduct]/9 中确实有一些措辞明确了这一点:

A lambda-expression appearing in a function type or a template parameter is not considered part of the immediate context for the purposes of template argument deduction. [Note: The intent is to avoid requiring implementations to deal with substitution failure involving arbitrary statements. [Example:

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

end example] — end note]

我们只是没有满足要求的东西。 gcc 的行为确实符合您的预期:

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

因为 lambda 的主体在直接上下文之外,所以这不是替换失败,而是硬错误。