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&)的用户给用户带来了很多问题,对我来说帮助追踪错误。
您可以包装
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);
}
如何使 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
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();
};