【问题标题】:Is lambdification of a concept an improvement or bad practice?一个概念的羔羊化是一种改进还是一种不好的做法?
【发布时间】:2022-01-19 10:49:58
【问题描述】:

看来您可以将 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 是一种改进还是一种不好的做法?从可读性的角度来看也是如此。

【问题讨论】:

  • 但是正如您发布的链接所示,这实际上不起作用。如果这个概念失败了,你会得到一个硬错误,因为 lambda 中的代码被实例化了。

标签: c++ c++20 c++-concepts


【解决方案1】:

这应该是无效的。允许 lambdas 进入未评估的上下文的目的不是突然允许 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 的主体在直接上下文之外,所以这不是替换失败,而是硬错误。

【讨论】:

  • 谢谢!那是我所缺少的,但懒得去在标准中查找它。
【解决方案2】:

忽略此机制中明显的可读性缺陷,它实际上工作。考虑以下几点:

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

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

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

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

即使它确实有效,它仍然无效。概念具有包含概念的复杂规则,这使得不同的概念被认为比其他概念更专业。这允许overloading on different concepts,它可以调用更受约束的概念。例如,只需要default_initializable 的概念比需要default_initializablemoveable 的概念更通用。因此,如果一个类型同时满足两者,则将采用后者,因为它受到更多约束。

但这仅适用于concepts 的特殊规则。在 lambdas 中隐藏要求不会让这个工作。

【讨论】:

  • 谢谢!更多我错过的东西。非常感激。希望我能接受两个答案。
猜你喜欢
  • 1970-01-01
  • 2014-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-24
相关资源
最近更新 更多