在下面的代码中,为什么允许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引用的函数时,它实际上意味着什么?
您可能知道,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。这对于有效地为x
和x&
使用相同的表达式来调用您的函数是有效的。如果不将临时值复制到新的prvalue临时值中,则无法转发prvalue-ness,这将破坏转发。
隐式转换可以更进一步:从double
到int
。事实上,如果您将浮动类型发送给一个使用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次,以下是结果。