自动添加 const 的 std::function 参数类型的类型推导

type deduction for std::function argument types with auto adds const

我有一个结构,它有一个名为 call 的方法,它有一个 const 重载。唯一的参数是 std::function,它根据重载接受 int 引用或 const int 引用。

genericCall 方法做完全相同的事情,但使用模板参数而不是 std::function 作为类型。

struct SomeStruct {
    int someMember = 666;

    void call(std::function<void(int&)> f) & {
        f(someMember);
        std::cout << "call: non const\n";
    }

    void call(std::function<void(const int&)> f) const& {
        f(someMember);
        std::cout << "call: const\n";
    }


    template <typename Functor>
    void genericCall(Functor f) & {
        f(someMember);
        std::cout << "genericCall: non const\n";
    }

    template <typename Functor>
    void genericCall(Functor f) const& {
        f(someMember);
        std::cout << "genericCall: const\n";
    }
};

当我现在创建此结构并使用 lambda 和 auto & 作为参数调用 call 时,尽管对象不是 const,std::function 总是推导出 const int &

另一方面,genericCall 在 lamdba 中将参数正确推导为 int &

SomeStruct some;
some.call([](auto& i) {
    i++;  // ?? why does auto deduce it as const int & ??
});
some.genericCall([](auto& i) {
    i++;  // auto deduces it correctly as int &
});

我完全不知道为什么 auto 在这两种情况下表现不同,或者为什么 std::function 似乎更喜欢在这里使参数为 const。尽管调用了正确的方法,但这会导致编译错误。当我将参数从 auto & 更改为 int & 时,一切正常。

some.call([](int& i) {
    i++; 
});

当我对结构的 const 版本执行相同的调用时,一切都按预期推导。 callgenericCall 都在这里推导出一个 const int &

const SomeStruct constSome;
constSome.call([](auto& i) {
    // auto deduces correctly const int & and therefore it should
    // not compile
    i++;
});
constSome.genericCall([](auto& i) {
    // auto deduces correctly const int & and therefore it should
    // not compile
    i++;
});

如果有人能对此有所启发,我将不胜感激!

对于那些想深入研究的更好奇的人,拉取请求中出现了这个问题:https://github.com/eclipse-iceoryx/iceoryx/pull/1324 在为 expected 实现实现功能接口时。

问题是通用 lambda(自动参数)等同于 operator() 模板化的可调用对象。这意味着 lambda 参数的实际类型不包含在 lambda 中,并且仅在调用 lambda 时推导。

但是在您的情况下,通过具有特定的 std::function 参数,您可以在调用 lambda 之前强制转换为具体类型,因此无法从任何内容中推断出自动类型。 non-template 上下文中没有 SFINAE。

没有特定的参数类型,您的 call 都是有效的重载。实际上任何可以匹配 [](auto&) 的 std::function 都是有效的。现在唯一的规则可能是最多 cv-qualified 过载获胜。您可以尝试使用 volatile float&,您会发现它仍然会选择那个。一旦选择此重载,尝试调用时编译将失败。

问题是尝试确定您的 lambda 是否为 Callableconst int & returning void 是一个硬错误,需要确定是否你可以构造一个 std::function<void(const int&)>.

您需要实例化 lambda 的主体以确定 return 类型。这不在替换模板参数的直接上下文中,所以它不是 SFINAE。

这是一个 equivalent error 实例化特征。

正如@aschepler 在评论中指出的那样,specifying a return type 消除了实例化 lambda 主体的需要。