当前位置: 首页 > 知识库问答 >
问题:

为什么允许签名为void(X)的std::函数绑定到void f(X&&)的函数?

胡博艺
2023-03-14

在下面的代码中,为什么允许std::function 绑定到函数void f(X&&)

#include <functional>
struct X {};

void f1(X x) {}
void f2(X& x) {}
void f3(const X&) {}
void f4(X&& x) {}

int main()
{
    X x;
    
    f1(x);  // ok
    f2(x);  // ok
    f3(x);  // ok
//    f4(x);  // doesn't compile

    std::function<void (X)> ff1(f1);    // ok
    //std::function<void (X)> ff2(f2);  // doesn't compile
    std::function<void (X)> ff3(f3);    // ok
    std::function<void (X)> ff4(f4);    // ok... why?

    return 0;
}

演示

为什么允许这样做的直觉是什么,std::function如何实现它,以及当您向std::function传递一个lvalue参数,然后它调用接受rvalue引用的函数时,它实际上意味着什么?

共有1个答案

沈博达
2023-03-14

您可能知道,std::function类型是任何类函数类型的多态包装器。其中包括函数指针、带有运算符()的类和lambdas。

由于它必须实现类型擦除,它只检查您发送的内容是否可以使用它接收的参数调用。类型擦除将简单地隐式执行转换,就像任何函数调用一样。

如果查看std::function::function的cppreference页面,您将注意到以下要求:

5)使用std::move(f)初始化目标。如果f是指向函数的空指针或指向成员的空指针,则*This在调用后将为空。除非f可用于参数类型args...和返回类型r调用,否则此构造函数不参与重载解析。

实际上,类型x的表达式完全可以绑定到类型x&的rvalue引用。试试看:

X&& x = X{};

// likewise, both prvalue and xvalue works:
f4(X{});
f4(std::move(x));

在本例中,x{}x类型的prvalue。rvalue-reference可以绑定到这样的临时引用。

std::function中,每个参数都被转发。转发不保留prvalue-ness,而是转发rvalue-ness。这对于有效地为xx&使用相同的表达式来调用您的函数是有效的。如果不将临时值复制到新的prvalue临时值中,则无法转发prvalue-ness,这将破坏转发。

隐式转换可以更进一步:从doubleint。事实上,如果您将浮动类型发送给一个使用int的函数,那么C++将执行隐式转换。std::function实现也不能免受这种影响。请考虑以下示例:

#include <functional>
#include <cstdio>

int main() {
    auto const f = std::function<void(double)>{
        [](int a) {
            std::printf("%d", a);
        }
    };

    f(9.8); // prints 9

    return 0;
}

联机编译器资源管理器

 类似资料:
  • Scott Meyers发布了他的下一本书EC 11的内容和状态。他写道,书中的一项可以是“在函数签名中避免”。 如果可以用作函数参数、返回类型或类模板或函数模板参数,则可以有条件地从重载解析中删除函数或类。 在这个问题中,显示了所有三种解决方案。 作为功能参数: 作为模板参数: 作为返回类型: 哪种解决方案应该是首选的,为什么我要避开其他人? 在哪些情况下,函数签名中避免d::estnable_

  • 问题内容: 我是一名编程初学者,对函数的返回值有疑问。 我正在学习Java。 我已经附上了我的书中具有经典选择排序功能的代码。 现在显然来自本书的代码可以正常工作。但是,主要功能中的以下三行是我的问题的基础: int [] a = new int [] {1,9,2,8,3,7,4,6​​,5}; 排序(a); if(ascending(a))System.out.println(“ Works”

  • 基于此,我认为我应该完全开始在中使用

  • 问题内容: 我刚接触PHP,但是多年来我一直在使用类似的语言进行编程。我被以下内容弄糊涂了: 它产生了语法错误:这就是调用。 但这很好用: 碰了一会儿之后,我被告知您不能在默认属性中调用函数。你必须在做。我的问题是:为什么?这是“功能”还是草率的实现?有什么根据? 问题答案: 编译器代码建议这是设计使然,尽管我不知道其背后的官方原因是什么。我也不确定要可靠地实现此功能需要花费多少精力,但是目前完成

  • 问题内容: 来自C语言的Go语言最值得注意的方面之一是,如果在其中声明了一个未使用的变量,编译器将不会编译您的程序。那么,如果在函数中声明了一个未使用的参数,那么为什么要构建此程序呢? 问题答案: 没有正式的原因,但是在golang-nuts上给出的原因是: 未使用的变量始终是编程错误,而编写不使用其所有参数的函数是很常见的。 可以将这些参数保留为未命名(使用_),但这可能会与诸如 func fo

  • 以下Python3.x整数乘法的平均运算时间在1.66s到1.77s之间: 如果将替换为,则需要在和之间。怎么会呢? 另一方面,在Java中则相反:在Java中更快。Java测试链接:为什么在Java中2*(i*i)比2*i*i快? 我运行每个版本的程序10次,以下是结果。