std::function 和 lambda 不符合参考要求

问题描述 投票:0回答:3
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);

我一半理解为什么

f1
f2
是有效的(和类似的)代码(但不是“按我想要的方式工作”)。

如何使

f1
编译失败,需要一个 int 引用?

如果

f2
失败或编译我不太在意,但如果它编译,自动变量应该是一个int引用而不是一个int。它成为一个 auto int ref 将是首选的出路。

f3
f4
按预期工作。

附录:std::function 中的引用在我的库代码中的许多地方使用。我想让上述图书馆的消费者很容易发现愚蠢的错误。我不介意在库中增加复杂性。我想避免让图书馆的消费者感到难过。在参数上使用“auto”而不是“auto&”(或 proper_type&)的用户给用户带来了很多问题,对我来说帮助追踪错误。

c++ std-function
3个回答
2
投票

您可以包装

int
使其不可复制,但这需要同时更改调用站点和函数:

#include <functional>

template <typename T>
struct uncopyable
{
    T value;
    uncopyable(T value) : value(value) {}
    uncopyable(const uncopyable &) = delete;
    uncopyable(uncopyable &&) = delete;
    uncopyable& operator=(const uncopyable &) = delete;
    uncopyable& operator=(uncopyable &&) = delete;
};

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

在coliru上看到它


1
投票

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

将 lambda 转换为您的类型。

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

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

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

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; }); // error

0
投票

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()); } ||
    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);
        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上看到它

© www.soinside.com 2019 - 2024. All rights reserved.