英文:
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<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);
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 <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);
}
答案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 <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
答案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 <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();
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论