使用 auto 添加 const 的 std::function 参数类型的类型推导

发布于 2025-01-19 20:29:30 字数 2453 浏览 3 评论 0原文

我有一个带有const超载的方法的结构,该结构称为call。一个也是唯一的参数是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&amp;作为参数创建此结构并调用调用时尽管对象不是const,但const int&amp; 。

另一方面,genericCall将参数正确推论为lamdba内部的int&amp;

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&amp;/code>更改为int&amp;时,一切都很好。

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

当我使用结构的const版本进行相同的调用时,所有内容都是按预期推导的。 callgenericCall在此处推论const int&amp;在此处。

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-eceoryx/iceoryx/pull/1324 在实现预期实现的功能接口时。

I have a struct with a method called call which has a const overload. The one and only argument is a std::function which either takes a int reference or a const int reference, depending on the overload.

The genericCall method does exactly the same thing but uses a template parameter instead of a std::function as type.

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";
    }
};

When I now create this struct and call call with a lambda and auto & as argument the std::function always deduces a const int & despite the object not being const.

The genericCall on the other hand deduces the argument correctly as int & inside the lamdba.

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 &
});

I have no the slightest clue why auto behaves in those two cases differently or why std::function seems to prefer to make the argument const here. This causes a compile error despite the correct method is called. When I change the argument from auto & to int & everything works fine again.

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

When I do the same call with a const version of the struct everything is deduced as expected. Both call and genericCall deduce a const int & here.

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++;
});

If someone could shine some light on this I would be very grateful!

For the more curious ones who want to dive even deeper, this problem arose in the pull request: https://github.com/eclipse-iceoryx/iceoryx/pull/1324 while implementing a functional interface for an expected implementation.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

再可℃爱ぅ一点好了 2025-01-26 20:29:30

问题在于,尝试确定您的 lambda 是否可调用是一个很难的错误 与 const int & 返回 void,这是确定是否可以构造一个std::function

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

这是实例化特征的等效错误

正如 @aschepler 在评论中指出的,指定返回类型无需实例化你的 lambda 的主体。

The issue is that it's a hard error to try to determine whether your lambda is Callable with const int & returning void, which is needed to determine whether you can construct a std::function<void(const int&)>.

You need to instantiate the body of the lambda to determine the return type. That's not in the immediate context of substituting a template argument, so it's not SFINAE.

Here's an equivalent error instantiating a trait.

As @aschepler notes in the comments, specifying a return type removes the need to instantiate the body of your lambda.

弥繁 2025-01-26 20:29:30

问题在于通用 lambda(auto param)相当于一个可调用对象,其 operator() 是模板化的。这意味着 lambda 参数的实际类型不包含在 lambda 中,并且仅在调用 lambda 时推导。

然而,在您的情况下,通过使用特定的 std::function 参数,您可以在调用 lambda 之前强制转换为具体类型,因此无法从任何内容推断出 auto 类型。非模板上下文中没有 SFINAE。

如果没有特定的参数类型,您的两个调用都是有效的重载。实际上,任何可以匹配 [](auto&) 的 std::function 都是有效的。现在唯一的规则可能是最符合简历资格的超载获胜。您可以尝试使用易失性浮点&,您会看到它仍然会选择它。一旦选择此重载,尝试调用时编译将失败。

The problem is that generic lambdas (auto param) are equivalent to a callable object whose operator() is templated. This means that the actual type of the lambda argument is not contained in the lambda, and only deduced when the lambda is invoked.

However in your case, by having specific std::function arguments, you force a conversion to a concrete type before the lambda is invoked, so there is no way to deduce the auto type from anything. There is no SFINAE in a non-template context.

With no specific argument type, both your call are valid overloads. Actually any std::function that can match an [](auto&) is valid. Now the only rule is probably that the most cv-qualified overload wins. You can try with a volatile float& and you will see it will still choose that. Once it choose this overload, the compilation will fail when trying to invoke.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文