std::function和lambda未遵守引用要求。

huangapple go评论57阅读模式
英文:

std::function and lambda not respecting reference requirement

问题

Sure, here is the translated code portion:

    using viref_func = std::function<void(int& intref)>;
    viref_func f1 = [](int foo) { ++foo; };
    viref_func f2 = [](auto foo) { ++foo; };
    viref_func f3 = [](int& foo) { ++foo; };
    viref_func f4 = [](auto& foo) { ++foo; };
    
    int test = 0;
    f1(test);
    f2(test);
    f3(test);
    f4(test);

If you have any specific questions about this code or need further assistance, please let me know.

英文:
using viref_func = std::function&lt;void(int&amp; intref)&gt;;
viref_func f1 = [](int foo) { ++foo; };
viref_func f2 = [](auto foo) { ++foo; };
viref_func f3 = [](int&amp; foo) { ++foo; };
viref_func f4 = [](auto&amp; foo) { ++foo; };

int test(0);
f1(test);
f2(test);
f3(test);
f4(test);

I half and half understand why f1 and f2 are valid (and analogous) code (but not "working as I want it to").

How can I make f1 fail compilation, requiring an int reference?

I don't care much if f2 fail or compiles, but if it compiles, the auto variable should be an int reference and not an int. It becoming an auto int ref would be the preferred way out.

f3 and f4 work as intended.

Addendum: references in std::function are used in my library code in a number of places. I want to make it easy on consumers of said library to catch dumb mistakes. I don't mind adding complexity in the library. I want to avoid making it hard on consumers of the library. A user having "auto" on a parameter rather than "auto&" (or proper_type&) caused a lot of problems at the user, and for me helping to track down the bug.

答案1

得分: 2

你可以将 int 包装成不可复制的,但这需要对调用点和函数都进行更改。

英文:

You can wrap int to be uncopyable, but that requires changes to both the call site and the functions:

#include &lt;functional&gt;

template &lt;typename T&gt;
struct uncopyable
{
    T value;
    uncopyable(T value) : value(value) {}
    uncopyable(const uncopyable &amp;) = delete;
    uncopyable(uncopyable &amp;&amp;) = delete;
    uncopyable&amp; operator=(const uncopyable &amp;) = delete;
    uncopyable&amp; operator=(uncopyable &amp;&amp;) = delete;
};

int main()
{
    using viref_func = std::function&lt;void(uncopyable&lt;int&gt;&amp; intref)&gt;;
    // viref_func f1 = [](int foo) { ++foo; }; // error as desired
    // viref_func f2 = [](auto foo) { ++foo; }; // also an error
    viref_func f3 = [](uncopyable&lt;int&gt;&amp; foo) { ++foo.value; };
    viref_func f4 = [](auto&amp; foo) { ++foo.value; };
    
    uncopyable&lt;int&gt; test(0);
    f3(test);
    f4(test);
}

See it on coliru

答案2

得分: 1

如何使f1编译失败,要求一个int引用?

将lambda表达式转换为您的类型。

#include <functional>
using viref_func_type = void (int&);
using viref_func = std::function<viref_func_type>;

viref_func_type *f11 = [](int foo) { ++foo; }; // 错误
viref_func f1 = f11;

viref_func f2 = static_cast<viref_func_type*>(&[](int foo) { ++foo; }); // 错误

template <typename> struct fn_sig;
template <typename T> struct fn_sig<std::function<T>> { using type = T; };
viref_func f3 = static_cast<fn_sig<viref_func>::type*>(&[](int foo) { ++foo; }); // 错误
英文:

> How can I make f1 fail compilation, requiring an int reference?

Cast the lambda to your type.

#include &lt;functional&gt;
using viref_func_type = void (int&amp; intref);
using viref_func = std::function&lt;viref_func_type&gt;;

viref_func_type *f11 = [](int foo) { ++foo; }; // error
viref_func f1 = f11;

viref_func f2 = static_cast&lt;viref_func_type*&gt;([](int foo) { ++foo; }); // error

template &lt;typename&gt; struct fn_sig;
template &lt;typename T&gt; struct fn_sig&lt;std::function&lt;T&gt;&gt; { using type = T; };
viref_func f3 = static_cast&lt;fn_sig&lt;viref_func&gt;::type*&gt;([](int foo) { ++foo; }); // error

答案3

得分: 1

std::function 允许在声明的参数和绑定的函数对象参数之间进行隐式转换(同样适用于结果类型)。您可以自己编写一个变种,要求参数完全匹配。

template <typename F, typename R, typename... Args>
concept exact_invokable = 
    requires () { static_cast<R(F::*)(Args...)>(&F::operator()); } ||
    requires () { static_cast<R(F::*)(Args...) const>(&F::operator()); } ||
    requires () { static_cast<R(F::*)(Args...) &&>(&F::operator()); } ||
    requires () { static_cast<R(F::*)(Args...) const &&>(&F::operator()); } ||
    std::same_as<F, R(*)(Args...)>;

template <typename F>
class exact_function;

template <typename R, typename... Args>
class exact_function<R(Args...)> : std::function<R(Args...)>
{
    template <typename F>
    static std::function<R(Args...)> to_function(F f)
    {
        if constexpr (requires () { static_cast<R(F::*)(Args...)>(&F::operator()); }) 
            return std::bind_front(static_cast<R(F::*)(Args...)>(&F::operator()), f);
        if constexpr (requires () { static_cast<R(F::*)(Args...) const>(&F::operator()); }) 
            return std::bind_front(static_cast<R(F::*)(Args...) const>(&F::operator()), f);
        if constexpr (requires () { static_cast<R(F::*)(Args...) &&>(&F::operator()); }) 
            return std::bind_front(static_cast<R(F::*)(Args...) &&>(&F::operator()), f);
        if constexpr (requires () { static_cast<R(F::*)(Args...) const &&>(&F::operator()); }) 
            return std::bind_front(static_cast<R(F::*)(Args...) const &&>(&F::operator()), f);
        return f;
    }

public:
    template<exact_invokable<R, Args...> F>
    exact_function(F f) : std::function<R(Args...)>(to_function(f)) {}
    
    using std::function<R(Args...)>::operator();
};

您可以在 coliru 上查看它

英文:

std::function allows implicit conversions between the declared parameters and bound functor's parameters (ditto result types). You could write a variation yourself that required an exact match.

template &lt;typename F, typename R, typename... Args&gt;
concept exact_invokable = 
requires () { static_cast&lt;R(F::*)(Args...)&gt;(&amp;F::operator()); } ||
requires () { static_cast&lt;R(F::*)(Args...) const&gt;(&amp;F::operator()); } ||
requires () { static_cast&lt;R(F::*)(Args...) &amp;&gt;(&amp;F::operator()); } ||
requires () { static_cast&lt;R(F::*)(Args...) const &amp;&gt;(&amp;F::operator()); } ||
std::same_as&lt;F, R(*)(Args...)&gt;;
template &lt;typename F&gt;
class exact_function;
template &lt;typename R, typename... Args&gt;
class exact_function&lt;R(Args...)&gt; : std::function&lt;R(Args...)&gt;
{
template &lt;typename F&gt;
static std::function&lt;R(Args...)&gt; to_function(F f)
{
if constexpr (requires () { static_cast&lt;R(F::*)(Args...)&gt;(&amp;F::operator()); }) 
return std::bind_front(static_cast&lt;R(F::*)(Args...)&gt;(&amp;F::operator()), f);
if constexpr (requires () { static_cast&lt;R(F::*)(Args...) const&gt;(&amp;F::operator()); }) 
return std::bind_front(static_cast&lt;R(F::*)(Args...) const&gt;(&amp;F::operator()), f);
if constexpr (requires () { static_cast&lt;R(F::*)(Args...) &amp;&gt;(&amp;F::operator()); }) 
return std::bind_front(static_cast&lt;R(F::*)(Args...) &amp;&gt;(&amp;F::operator()), f);
if constexpr (requires () { static_cast&lt;R(F::*)(Args...) const &amp;&gt;(&amp;F::operator()); }) 
return std::bind_front(static_cast&lt;R(F::*)(Args...) const &amp;&gt;(&amp;F::operator()), f);
return f;
}
public:
template&lt;exact_invokable&lt;R, Args...&gt; F&gt;
exact_function(F f) : std::function&lt;R(Args...)&gt;(to_function(f)) {}
using std::function&lt;R(Args...)&gt;::operator();
};

See it on coliru

huangapple
  • 本文由 发表于 2023年4月13日 20:58:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/76005725.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定