【问题标题】:std::bind works with decltype type of class member functions failedstd::bind 使用 decltype 类型的类成员函数失败
【发布时间】:2019-12-02 18:41:30
【问题描述】:

我正在使用 decltype 和 std::remove_pointer 来声明类型类,并希望将其传递给 std::bind。

class Result { 
public:
    Result(){}
    ~Result(){}
    std::string get(const std::string& name) { return name; } 
}; 

typedef std::function<std::string()> MyGet; 

int main() {
    // OK
    Result result1;
    MyGet mg1 = std::bind(&Result::get, &result1, std::cref("result1"));
    std::cout << mg1() << std::endl;
    // OK
    Result result2;
    MyGet mg2 = std::bind(&decltype(result2)::get, &result2, std::cref("result2"));
    std::cout << mg2() << std::endl;
    // Failed
    Result* result3 = new Result;
    MyGet mg3 = std::bind(&decltype(std::remove_pointer(result3))::get, &result3,
std::cref("result3"));
    std::cout << mg3() << std::endl;
    delete result3;

    return 1; 
}

代码的 result3 部分显示此错误: 不能在没有模板参数列表的情况下引用类模板“remove_pointer” 如何解决?

【问题讨论】:

  • 只是好奇:为什么选择std::bind 而不是使用 lambda?
  • std::remove_pointer 作用于类型,而不是表达式(这就是为什么语法是 typename std::remove_pointer&lt;SomeType&gt;::type,而不是 std::remove_pointer(SomeExpression)
  • 你的意思是 result3 是 SomeExpression 吗?这也是构建失败:MyGet mg3 = std::bind(&std::remove_pointer(Result*)::type::get, &result3, std::cref("result3"));
  • 使用 lambda:auto mg1 = [result1] () { return result1.get ("result1"); }; 并声明 get const
  • 你为什么要尝试使用remove_pointer 来获取result1 指向的类型,而你已经知道该类型?这只是Result

标签: c++ functional-programming


【解决方案1】:

正确的语法是:

Result* result3 = new Result;
MyGet mg3 = std::bind(
    &std::remove_pointer_t<decltype(result3)>::get, result3, std::cref("result3"));

MyGet mg3 = std::bind(
    &std::remove_pointer<decltype(result3)>::type::get, result3, std::cref("result3"));

std::remove_pointer 作用于类型,而不是表达式。使用decltype获取result3的类型,然后去掉指针。

另外,你当然应该直接传递result3,而不是指向它的指针(它已经是一个指针)。

演示:https://godbolt.org/z/Rf2PpB

【讨论】:

  • 但是我们已经知道remove_pointer&lt;decltype(result3)&gt;的类型……它是Result
  • @JonathanWakely 我们也已经知道程序会输出什么,但我们还是通过了std::bind 的歌舞。这里隐含的假设是 result3 的类型事先不知道,为了简单起见,只是以这种方式呈现。我知道这可能会涉及模板,所以会有一些 typename 恶作剧(这是另一个使用 lambdas 的好理由)。
  • 在第二个选项中(用于c++11),std::remove_pointer&lt;decltype(result3)&gt;::type::get不应该有typename,因为::type是依赖的?
  • @JeJo 仅当我们在模板函数中时。 main 不是模板函数,因此没有两阶段查找和依赖名称。但如上所述,您不太可能在模板之外需要std::remove_pointer,这就是这段代码看起来很奇怪的原因。
  • @MaxLanghof 哦..是的。我没有阅读cmets并寻求答案。顺便说一句,我也不知道这个!
【解决方案2】:

您上次的std::bind 通话存在许多问题:

  • std::remove_pointer(result3)

remove_pointer 是一个类。你想要remove_pointer_t,它将产生括号中的类型无指针,这会导致下一个点

  • result3Result*instance,我们需要使用decltype来获取它的类型,因为remove_pointer对类型进行操作,这就引出了下一点:

  • std::remove_pointer 作为模板类型特征希望 type 作为 模板参数 在尖括号中,而不是在括号中

应用编辑后,我们会看到以下语法

MyGet mg3 = std::bind(&std::remove_pointer_t<decltype(result3)>::get, result3, std::cref("result3"));

Demo


最后,lambda 可能更容易处理。你根本不需要构造std::function

Result result1;
auto mg1 = [&result1](){return result1.get("result1");};
std::cout << mg1() << std::endl;
Result result2;
auto mg2 = [&result2](){return result2.get("result2");};
std::cout << mg2() << std::endl;
Result* result3 = new Result;
auto mg3 = [result3](){return result3->get("result3");};
std::cout << mg3() << std::endl;
delete result3;

Lambda demo

我强烈推荐STL's CppCon 2015 talk 来解释为什么bind() 几乎被弃用,取而代之的是 lambda。

【讨论】:

    【解决方案3】:
    1. 你应该先在变量上使用decltype获取类型,然后使用remove_pointer获取你想要的类型。

    2. result3 本身就是一个指针,你应该传递result3*result3 而不是&amp;result3

    例如

    MyGet mg3 = std::bind(&std::remove_pointer<decltype(result3)>::type::get, result3, std::cref("result3"));
    

    【讨论】:

    • 值得一提的是,*result3 将在绑定结果中存储一个副本,这可能不是预期的(无论如何,这不是 OP 代码中 bind 的前两次使用所做的)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-06
    • 1970-01-01
    • 2013-01-08
    相关资源
    最近更新 更多