2016-11-11 47 views
1

我有类似于Passing different lambdas to function template in c++的问题,但现在使用由std::bind创建的包装来代替lambda表达式。将std :: bind的结果传递给std :: function“overloads”

我有方法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) 
{ 
} 

现在,这正常工作与lambda表达式,但未能与绑定std::bind函子:

struct Predicates 
{ 
    bool Predicate0() { return true; } 
    bool Predicate1(const Value<int> &) { return true; } 
}; 

Predicates p; 
Add(i, std::bind(&Predicates::Predicate0, &p)); 

失败

错误C2668:'添加':模糊呼叫overloa DED功能

Add(i, std::bind(&Predicates::Predicate1, &p, _1)); 

失败,静态断言(的Visual C++ 2015年,更新3):

元组索引越界

是否有办法使它与lambda表达式和绑定函子一起工作?我会考虑使用SFINAE来启用基于is_bindable_expression的单个过载并检查参数类型,但我没有把它放在一起。

+1

'std :: bind(&Predicates :: Predicate0,&p)()'和'std :: bind(&Predicates :: Predicate0,&p)(值为 {})'都是有效的。 (在后一种情况下,参数Value {}'被忽略。)这是'std :: bind'的一个特性。 – cpplearner

+1

您滥用超载。满足属性的类型“可以用此参数调用它的实例”或“可以用另一个参数调用它的实例”的类型是非空的。 – milleniumbug

回答

2

停止使用std::bind

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>>{}); 
} 

与标签定义:在我看来,这会是更好的可读性派遣上正确命名的标记。这是一个随机的功能和怪癖混乱。

今天的怪癖是,std::bind将接受无限数量的参数,并放弃任何额外的。明天你可能会遇到这样的事实,即通过std::bind结果std::bind做奇怪的魔法。

std::bind被移植到boost在同一时间lambda添加到语言。 Lambdas几乎解决了bind几乎所有问题,只是语法清晰,并且没有遇到怪异bind的问题,尤其是在C++ 14后auto lambdas可用时。 (大多数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检测上要使用两个重载的,并明确偏爱一个。

这不值得。

+0

+1不鼓励使用'std :: bind',特别是因为我们有C++ 14 lambdas和'std :: bind'实际上是来自boost的那个兄弟。我试图想出一个更喜欢绑定lambdas的理由,但我提出的唯一一个半好的不是类型擦除,用于创建事件处理程序序列的static-return-type,但这并不意味着太多如果你不能拼写那种类型的话。 – krzaq

+1

@krzaq在某些情况下,内联头文件中的lambda返回函数存在ODR问题。我希望C++标准委员会能够处理它们,它们不会触发(几乎总是完全无害的)UB。 – Yakk

+0

@Yakk我不知道有关'std :: bind'的问题,我将尽量避免它。不过,我尝试了你讨厌的SFINAE检测,看起来它无法正常工作。在VC++中,我遇到了一个麻烦,那就是在STL和GCC中用静态断言“元组索引越界”来编译它,而编译好的时候,结果并不是我所期望的。看起来,用不同绑定方法调用'Add'最终会调用相同的过载。请参阅http://melpon.org/wandbox/permlink/pcwxYAQcaoSxFQNi – manison

3

我不认为你可以做你想做的。

您可以使用is_bind_expression来检查您的参数是否是通过调用std::bind产生的类型,但无法确定callable期望的参数数量。作为cpplearned在评论中提到的,这是std::bind一个特点:

如果某些被呼叫提供给g()参数不是 由存储在g任何占位符匹配,未使用的参数是 进行评价并丢弃。

这意味着两个重载同等有效。


如果你不介意分享相同的过载为所有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>>{}); 
} 

demo

但这类似于使用布尔参数。作为

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 {}; 

} 

demo

+0

然后,我可以利用此功能,并且只有一个方法'Add'用'function'服用论点: \t模板 \t空隙添加(价值&值,block_deduction <函数<布尔(常量值&) >>谓词); 并始终使用参数调用谓词。如果参数与绑定函数中的占位符不匹配,则该参数将被忽略。然而它打破了lambda。这是一个好的C++解决方案吗?你会建议什么? – manison

+0

我不知道你的用例,它可能是正确的,但它也可能是过度工程。这就是说,如果你可以放弃这个论点,我会跟着一个例子,当我回家时(2-3小时) – krzaq

+0

@manison检查答案。希望你会发现这有帮助。 – krzaq

相关问题