混用概念是一种改进还是不良做法?

问题描述

看来您可以在概念中放入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>;

羊羔化是一种进步还是不良做法?从可读性角度来看。

解决方法

忽略这种机制中明显的可读性缺陷,实际上并没有奏效。请考虑以下内容:

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的主体。那里,格式错误的代码是格式错误的,因此在尝试实例化该代码时会遇到编译错误。

即使它起作用了,它仍然不起作用。概念具有复杂的概念包含规则,因此可以认为不同的概念比其他概念更加专业。这样就可以使用overloading on different concepts,它可以调用更受约束的概念。例如,仅需要default_initializable的概念比需要default_initializablemoveable的概念更为通用。因此,如果一个类型同时满足这两种要求,则会采用后者,因为它受到的限制更大。

但这仅适用于concept的特殊规则。在lambdas中隐藏要求将使其无法正常工作。

,

这无效。允许lambda进入未评估上下文的目的并不是突然允许SFINAE用于语句。

我们在[temp.deduct]/9中确实有明确的措辞:

出于模板自变量推导的目的,在函数类型或模板参数中出现的 lambda-表达式不被视为立即上下文的一部分。 [注意:目的是避免要求实现处理涉及任意语句的替换失败。 [示例

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

由于lambda的主体位于即时上下文之外,因此这不是替换失败,而是一个硬错误。