将 std::bind 的结果传递给 std::function "overloads"
Passing result of std::bind to std::function "overloads"
我有类似 的问题,但现在使用 std::bind
而不是 lambda 创建的包装器。
我有两个重载方法 Add
,它们采用不同形式的 std::function
:
template<typename T>
struct Value
{
T value;
};
template <typename T>
void Add(Value<T> &value, function<bool()> predicate)
{
}
template <typename T>
void Add(Value<T> &value, block_deduction<function<bool(const Value<T> &)>> predicate)
{
}
现在这对 lambdas 可以正常工作,但对与 std::bind
:
绑定的仿函数会失败
struct Predicates
{
bool Predicate0() { return true; }
bool Predicate1(const Value<int> &) { return true; }
};
Predicates p;
Add(i, std::bind(&Predicates::Predicate0, &p));
失败
error C2668: 'Add': ambiguous call to overloaded function
和
Add(i, std::bind(&Predicates::Predicate1, &p, _1));
静态断言失败(Visual C++ 2015,更新 3):
tuple index out of bounds
有没有办法让它同时适用于 lambda 和绑定仿函数?我会考虑使用 SFINAE 启用基于 is_bindable_expression
的单独重载并检查参数类型,但我无法将它们放在一起。
我不认为你可以为所欲为。
您可以使用 is_bind_expression
来检查您的参数是否是由调用 std::bind
生成的类型,但是无法判断可调用对象需要多少个参数。正如 cpplearned 在评论中提到的,这是 std::bind
:
的一个特性
If some of the arguments that are supplied in the call to g()
are not
matched by any placeholders stored in g
, the unused arguments are
evaluated and discarded.
这意味着两个重载同样有效。
如果您不介意为所有 bind
结果共享相同的重载,您可以传递所有参数并让它们随意丢弃:
template <typename T>
void AddImpl(Value<T> &value, function<bool()> predicate, std::false_type)
{
predicate();
}
template <typename T>
void AddImpl(Value<T> &value, block_deduction<function<bool(const Value<T> &)>> predicate, std::false_type)
{
predicate(value);
}
template <typename T, typename U>
void AddImpl(Value<T>& value, U&& bind_expression, std::true_type)
{
bind_expression(value);
}
template<typename T, typename U>
void Add(T&& t, U&& u)
{
AddImpl(std::forward<T>(t), std::forward<U>(u), std::is_bind_expression<std::decay_t<U>>{});
}
但这类似于使用布尔参数。在我看来,在正确命名的标签上分派可读性更好:
template <typename T>
void AddImpl(Value<T> &value, function<bool()> predicate, tag::default_)
{
predicate();
}
template <typename T>
void AddImpl(Value<T> &value, block_deduction<function<bool(const Value<T> &)>> predicate, tag::default_)
{
predicate(value);
}
template <typename T, typename U>
void AddImpl(Value<T>& value, U&& bind_expression, tag::bind)
{
bind_expression(value);
}
template<typename T, typename U>
void Add(T&& t, U&& u)
{
AddImpl(std::forward<T>(t), std::forward<U>(u), tag::get_tag<std::decay_t<U>>{});
}
标签定义为
namespace tag
{
struct default_{};
struct bind{};
template<typename T, typename = void>
struct get_tag : default_ {};
template<typename T>
struct get_tag<T, std::enable_if_t<std::is_bind_expression<T>::value>> : bind {};
}
停止使用 std::bind
。这是一堆乱七八糟的随机功能和怪癖。
今天的怪癖是 std::bind
将接受无限数量的参数并丢弃任何额外的参数。明天你可能 运行 了解到将 std::bind
结果传递给 std::bind
会产生奇怪的魔法。
std::bind
被移植到 boost
,同时 lambdas 添加到语言中。 Lambda 解决了几乎所有问题 bind
用同样清晰的语法解决了几乎所有问题,并且没有 bind
做的无数怪癖,尤其是 post C++14 当 auto
lambda 可用时. (大多数 C++11 编译器也支持 auto
lambda)。
您可以编写函数,以便在两者都适用时将其中一个作为首选重载。但是这样做会给你的界面增加一堆噪音,在这种情况下,你想要这种偏好的唯一原因是因为 std::bind
正在做一些愚蠢的事情。
围绕 std
库中设计不佳的部分进行工程设计是不值得的。只需停止使用 std
库中设计不佳的部分,或在使用时显式转换。
做不到这一点,这样做:
template <class T, class F,
std::enable_if_t<
std::is_convertible<
std::result_of_t<std::decay_t<F> const&(Value<T> const&)>,
bool
>{}, int
> = 0
>
void Add(Value<T> &value, F&& f)
{
// do pass f Value<T>
}
template <class T, class F,
std::enable_if_t<
!std::is_convertible<
std::result_of_t<std::decay_t<F> const&(Value<T> const&)>,
bool
>{}
&& std::is_convertible<
std::result_of_t<std::decay_t<F> const&()>,
bool
>{}, int
> = 0
>
void Add(Value<T> &value, F&& f)
{
// do not pass f Value<T>
}
我们对您想使用的两个重载中的哪一个进行一些讨厌的 SFINAE 检测,并明确更喜欢一个。
这不值得。
我有类似 std::bind
而不是 lambda 创建的包装器。
我有两个重载方法 Add
,它们采用不同形式的 std::function
:
template<typename T>
struct Value
{
T value;
};
template <typename T>
void Add(Value<T> &value, function<bool()> predicate)
{
}
template <typename T>
void Add(Value<T> &value, block_deduction<function<bool(const Value<T> &)>> predicate)
{
}
现在这对 lambdas 可以正常工作,但对与 std::bind
:
struct Predicates
{
bool Predicate0() { return true; }
bool Predicate1(const Value<int> &) { return true; }
};
Predicates p;
Add(i, std::bind(&Predicates::Predicate0, &p));
失败
error C2668: 'Add': ambiguous call to overloaded function
和
Add(i, std::bind(&Predicates::Predicate1, &p, _1));
静态断言失败(Visual C++ 2015,更新 3):
tuple index out of bounds
有没有办法让它同时适用于 lambda 和绑定仿函数?我会考虑使用 SFINAE 启用基于 is_bindable_expression
的单独重载并检查参数类型,但我无法将它们放在一起。
我不认为你可以为所欲为。
您可以使用 is_bind_expression
来检查您的参数是否是由调用 std::bind
生成的类型,但是无法判断可调用对象需要多少个参数。正如 cpplearned 在评论中提到的,这是 std::bind
:
If some of the arguments that are supplied in the call to
g()
are not matched by any placeholders stored ing
, the unused arguments are evaluated and discarded.
这意味着两个重载同样有效。
如果您不介意为所有 bind
结果共享相同的重载,您可以传递所有参数并让它们随意丢弃:
template <typename T>
void AddImpl(Value<T> &value, function<bool()> predicate, std::false_type)
{
predicate();
}
template <typename T>
void AddImpl(Value<T> &value, block_deduction<function<bool(const Value<T> &)>> predicate, std::false_type)
{
predicate(value);
}
template <typename T, typename U>
void AddImpl(Value<T>& value, U&& bind_expression, std::true_type)
{
bind_expression(value);
}
template<typename T, typename U>
void Add(T&& t, U&& u)
{
AddImpl(std::forward<T>(t), std::forward<U>(u), std::is_bind_expression<std::decay_t<U>>{});
}
但这类似于使用布尔参数。在我看来,在正确命名的标签上分派可读性更好:
template <typename T>
void AddImpl(Value<T> &value, function<bool()> predicate, tag::default_)
{
predicate();
}
template <typename T>
void AddImpl(Value<T> &value, block_deduction<function<bool(const Value<T> &)>> predicate, tag::default_)
{
predicate(value);
}
template <typename T, typename U>
void AddImpl(Value<T>& value, U&& bind_expression, tag::bind)
{
bind_expression(value);
}
template<typename T, typename U>
void Add(T&& t, U&& u)
{
AddImpl(std::forward<T>(t), std::forward<U>(u), tag::get_tag<std::decay_t<U>>{});
}
标签定义为
namespace tag
{
struct default_{};
struct bind{};
template<typename T, typename = void>
struct get_tag : default_ {};
template<typename T>
struct get_tag<T, std::enable_if_t<std::is_bind_expression<T>::value>> : bind {};
}
停止使用 std::bind
。这是一堆乱七八糟的随机功能和怪癖。
今天的怪癖是 std::bind
将接受无限数量的参数并丢弃任何额外的参数。明天你可能 运行 了解到将 std::bind
结果传递给 std::bind
会产生奇怪的魔法。
std::bind
被移植到 boost
,同时 lambdas 添加到语言中。 Lambda 解决了几乎所有问题 bind
用同样清晰的语法解决了几乎所有问题,并且没有 bind
做的无数怪癖,尤其是 post C++14 当 auto
lambda 可用时. (大多数 C++11 编译器也支持 auto
lambda)。
您可以编写函数,以便在两者都适用时将其中一个作为首选重载。但是这样做会给你的界面增加一堆噪音,在这种情况下,你想要这种偏好的唯一原因是因为 std::bind
正在做一些愚蠢的事情。
围绕 std
库中设计不佳的部分进行工程设计是不值得的。只需停止使用 std
库中设计不佳的部分,或在使用时显式转换。
做不到这一点,这样做:
template <class T, class F,
std::enable_if_t<
std::is_convertible<
std::result_of_t<std::decay_t<F> const&(Value<T> const&)>,
bool
>{}, int
> = 0
>
void Add(Value<T> &value, F&& f)
{
// do pass f Value<T>
}
template <class T, class F,
std::enable_if_t<
!std::is_convertible<
std::result_of_t<std::decay_t<F> const&(Value<T> const&)>,
bool
>{}
&& std::is_convertible<
std::result_of_t<std::decay_t<F> const&()>,
bool
>{}, int
> = 0
>
void Add(Value<T> &value, F&& f)
{
// do not pass f Value<T>
}
我们对您想使用的两个重载中的哪一个进行一些讨厌的 SFINAE 检测,并明确更喜欢一个。
这不值得。