Ant*_*o42 4 c++ compile-time c++20
我试图在编译时验证给定的 lambda 是否接受某种类型(在我的示例代码中为双倍)。只要 lambda 的签名明确指定了它的工作类型。但是,一旦我在签名中使用带有 auto 的通用 lambda,在评估 requires 语句时就会出现编译错误。
下面的代码片段说明了这个问题(也在编译器资源管理器上)
#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
}
Run Code Online (Sandbox Code Playgroud)
我的印象是,requires 语句会验证 f(d); 是一个有效的表达式,如果不是,则返回 false。但是,在使用最新的 gcc 和 clang 进行编译时,我收到一条错误消息,表明它正在尝试评估函数体:
:13:38: error: no viable conversion from 'double' to 'std::string' (aka 'basic_string<char>')
Run Code Online (Sandbox Code Playgroud)
我的问题是否有不同的方法来确保 lambda 可以传递双精度值,或者需要语句仅限于检查签名?
这是预期的行为。
lambdaf3声称接受任何东西。但是为了测试它是否可以用 a 调用double,我们需要实例化调用运算符。只有在该实例化的“直接上下文”中的失败才算作替换失败——其中直接上下文基本上是与实际函数签名密切相关的任何东西。一旦我们进入调用运算符的主体,那将不再是直接上下文。我们必须实例化主体,这会失败(无法string从 a构造a double),但这是一个硬编译器错误。
换句话说,这不是 SFINAE 友好的。
现在,需要实例化 lambda 的主体的唯一原因是因为它返回auto并且我们需要知道返回类型。我们可以直接提供:
auto f3 = [](auto a) -> void {std::string b = a;};
Run Code Online (Sandbox Code Playgroud)
到这里,accepts_double<decltype(f3)>编译。但它给了true!因为 lambda 确实声称接受一切。这里完全没有限制。由于我们碰巧不必实例化主体,我们才免于编译失败。
如果我们想确保这两者都能编译并给出正确的答案,我们需要添加该约束:
auto f3 = [](std::convertible_to<std::string> auto a) {std::string b = a;};
Run Code Online (Sandbox Code Playgroud)
现在,我们实际上将参数限制为允许的类型集。这既可以编译又可以正确产生false(因为,当然,double不能转换为std::string)。