【问题标题】:C++20 concept / requires expression to test if generic lambda accepts typeC++20 概念/需要表达式来测试泛型 lambda 是否接受类型
【发布时间】:2021-02-16 13:47:29
【问题描述】:

我试图在编译时验证给定的 lambda 是否接受某种类型(在我的示例代码中为双精度)。只要 lambda 的签名明确指定了此工作的类型。但是,一旦我在签名中使用带有 auto 的通用 lambda,在评估 requires 语句时就会出现编译错误。

以下代码 sn -p 说明了问题(also on compiler explorer)

#include <concepts>
#include <iostream>
#include <string>

template<typename F>
concept accepts_double = requires(F f, double d) { f(d); };

struct Component{};

int main(){
    auto f1 = [](double a){double b = a;};
    auto f2 = [](std::string a){std::string b = a;};
    auto f3 = [](auto a){std::string b = a;};

    std::cout << std::boolalpha << accepts_double<decltype(f1)> << "\n"; // expected true
    std::cout << std::boolalpha << accepts_double<decltype(f2)> << "\n"; // expected false

//This one gives the error:
    std::cout << std::boolalpha << accepts_double<decltype(f3)> << "\n"; // expected false, gives compilation error
}

我的印象是 requires 语句将验证 f(d);是一个有效的表达式,如果不是,将返回 false。但是,当使用最新的 gcc 和 clang 进行编译时,我收到一个错误,表明它正在尝试评估函数体:

:13:38: error: no viable conversion from 'double' to 'std::string' (aka 'basic_string<char>')

我的问题是否有不同的方法来确保 lambda 可以传递一个双精度值,或者要求语句仅限于检查签名?

【问题讨论】:

  • 我认为这可能是一个错误。当我们使用带有operator() 模板的手工仿函数时,这个概念就通过了。成员的定义没有被实例化。

标签: c++ c++20 compile-time


【解决方案1】:

这是预期的行为。

lambda f3 声称接受任何东西。但是要测试它是否可以用double 调用,我们需要实例化调用运算符。只有该实例化的“直接上下文”中的失败才算作替换失败——其中直接上下文基本上与实际函数签名密切相关。一旦我们进入调用操作符的主体,它就不再是直接的上下文了。我们必须实例化主体,这会失败(无法从 double 构造 string),但这是一个硬编译器错误。

换句话说,这对 SFINAE 不友好。


现在,需要实例化 lambda 主体的唯一原因是因为它返回 auto,我们需要知道返回类型。我们可以直接提供:

auto f3 = [](auto a) -> void {std::string b = a;};

在这里,accepts_double&lt;decltype(f3)&gt; 编译。但它给了true!因为 lambda 确实声称接受一切。这里根本没有任何限制。我们只是因为碰巧不必实例化主体而免于编译失败。

如果我们想确保它既编译又给出正确答案,我们需要添加该约束:

auto f3 = [](std::convertible_to<std::string> auto a) {std::string b = a;};

现在,我们实际上将参数限制为允许的类型集。这可以编译并正确生成false(当然,double 不能转换为std::string)。

【讨论】:

  • 感谢您的详细解释。这确实解释了我所看到的行为。对于我正在从事的项目,我接受 lambda 作为参数并且无法控制其定义。然后我需要确定它是否接受类型 A 或类型 B(我对任何其他类型的编译错误都很好)。但是,从您的回答来看,在接受通用 lambda 时似乎无法实现这个目标?你能证实我的推理吗?
  • @Antiro42 是的。由 lambda 以某种方式约束自己。如果没有,那么您将无法检查任何内容 - 您会得到“是的,它可以工作”或“编译错误”。
猜你喜欢
  • 1970-01-01
  • 2021-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-07
相关资源
最近更新 更多