将std :: bind的结果传递给std :: function"重载"

时间:2016-11-11 12:17:11

标签: c++ c++14 template-meta-programming sfinae

我遇到与Passing different lambdas to function template in c++类似的问题,但现在使用由std::bind而不是lambdas创建的包装器。

我有两个方法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));

失败
  

错误C2668:&#39;添加&#39;:对重载功能的模糊调用

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

使用静态断言失败(Visual C ++ 2015,Update 3):

  

元组索引越界

有没有办法让它与lambdas和绑定仿函数一起工作?我会考虑使用SFINAE来启用基于is_bindable_expression的单个重载并检查参数类型,但我没有把它放在一起。

2 个答案:

答案 0 :(得分:3)

我认为你不能做你想做的事。

您可以使用is_bind_expression来检查您的参数是否是通过调用std::bind生成的类型,但无法确定调用期望的参数数量。正如评论中提到的那样,这是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

但这与使用布尔参数类似。在我看来,可读性更好地分配正确命名的标签:

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

}

demo

答案 1 :(得分:2)

停止使用std::bind。这是乱七八糟的随机特征和怪癖。

今天的怪癖是std::bind将接受无限数量的参数并丢弃任何额外的参数。明天你可能会遇到这样的事实:将std::bind结果传递给std::bind会产生奇怪的魔力。

boost同时移植到bind lambdas添加到语言中。 Lambdas解决了几乎所有问题bind在语法清晰的情况下所做的事情,并且没有对auto的怪癖感到害怕,特别是当auto lambdas可用时,特别是在C ++ 14之后。 (大多数C ++ 11编译器也支持std::bind lambda)。

您可以编写函数,以便在它们都适用时,其中一个或另一个是首选的重载。但这样做会给你的界面增加一堆噪音,在这种情况下,你想要这种偏好的唯一原因是因为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> } 库,或在使用时明确使用。

如果做不到,请执行此操作:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int read_dict() {
    FILE *fp;
    int c;
    char word[50];
    char defn[500];
    int sep = 0;
    int doublenew = 0;
    int i = 0;

    fp = fopen("textfile.txt", "r");
    if (fp == NULL) {
        perror("Error in opening file");
        return (-1);
    }

    while ((c = fgetc(fp)) != EOF) {
        if (feof(fp)) {
            break;
        }
        if (c == '.' && sep == 0) {
            sep = 1;
            word[i] = '\0';
            //c = fgetc(fp);
            i = 0;
        } else
        if (doublenew == 1 && c == '\n' && sep == 1) {
            defn[i] = c;
            i++;
            defn[i] = '\0';
            printf("%s %s", word, defn);
            i = 0;
            sep = 0;
            doublenew = 0;
        } else
        if (c == '\n' && sep == 1) {
            defn[i] = c;
            doublenew = 1;
            i++;
        } else
        if (sep == 0) {
            word[i] = c;
            i++;
        } else
        if (sep == 1) {
            defn[i] = c;
            i++;
            doublenew = 0;
        }
    }
    fclose(fp);
    return 0;
}

我们会在你想要使用的两个重载中抛出一些令人讨厌的SFINAE检测,并明确地选择一个。

这不值得。