在libc++的实现中std::function,如果要擦除其类型的函数对象足够小以适合 SBO,则移动操作将复制它,而不是移动它。然而,并不是每个堆栈内存占用较小的对象都适合复制。为什么要复制而不是移动?
使用 Clang 考虑这个示例(使用shared_ptr它是因为它具有引用计数):
https://wandbox.org/permlink/9oOhjigTtOt9A8Nt
中的语义与使用显式副本test1()的语义相同。帮助我们看到这一点。test3()shared_ptr
另一方面,GCC 的行为是合理且可预测的:
https://wandbox.org/permlink/bYUDDr0JFMi8Ord6
两者都是标准允许的。std::function要求函数可复制,移出的对象处于未指定状态,等等。为什么要这么做?同样的推理也适用于std::map:如果键和值都是可复制的,那么为什么不每当有人std::movesa时就制作一个新副本std::map?这也符合标准的要求。
根据cppreference.com 的说法,应该有一个举动,并且应该是目标。
这个例子:
#include <iostream>
#include <memory>
#include <functional>
#include <array>
#include <type_traits>
void test1()
{
/// Some small tiny type of resource. Also, `shared_ptr` is used because it has a neat
/// `use_count()` feature that will allow us to see what's going on behind the 'curtains'.
auto foo …Run Code Online (Sandbox Code Playgroud) 在c ++ 11中传递lambda非常简单:
func( []( int arg ) {
// code
} ) ;
Run Code Online (Sandbox Code Playgroud)
但我想知道,将lambda传递给像这样的函数的成本是多少?如果func将lambda传递给其他函数怎么办?
void func( function< void (int arg) > f ) {
doSomethingElse( f ) ;
}
Run Code Online (Sandbox Code Playgroud)
lambda的传递是否昂贵?由于可以为function对象分配0,
function< void (int arg) > f = 0 ; // 0 means "not init"
Run Code Online (Sandbox Code Playgroud)
它让我认为函数对象就像指针一样.但是,如果不使用new,则意味着它们可能类似于值类型struct或类,它们默认为堆栈分配和成员方式副本.
当你按"值"传递一个函数对象时,C++ 11"代码体"和捕获的变量组是如何传递的?是否有很多代码体的多余副本?我是否必须标记function传递的每个对象,const&以便不进行复制:
void func( const function< void (int arg) >& f ) {
}
Run Code Online (Sandbox Code Playgroud)
或者以某种方式使函数对象以不同于常规C++结构的方式传递?
以下程序不编译:
#include <iostream>
#include <vector>
#include <functional>
#include <algorithm>
#include <cstdlib>
#include <cmath>
void asort(std::vector<double>& v, std::function<bool(double, double)> f)
{
std::sort(v.begin(), v.end(), [](double a, double b){return f(std::abs(a), std::abs(b));});
}
int main()
{
std::vector<double> v({1.2, -1.3, 4.5, 2.3, -10.2, -3.4});
for (unsigned int i = 0; i < v.size(); ++i) {
std::cout<<v[i]<<" ";
}
std::cout<<std::endl;
asort(v, [](double a, double b){return a < b;});
for (unsigned int i = 0; i < v.size(); ++i) {
std::cout<<v[i]<<" ";
}
std::cout<<std::endl;
return 0;
} …Run Code Online (Sandbox Code Playgroud) 所有报价均来自N3797.
4/3 [转]
当且仅当声明T t = e时,表达式e可以隐式转换为类型T. 对于一些发明的临时变量t而言,其形式良好
这意味着没有表达式可以隐式转换为void,这void t=e对所有表达式都是非法的e.如果e是类型的表达式void,例如,这甚至是真的void(3).
所以类型的表达式void不能隐式转换为void.
这导致我们:
20.9.2/2要求[func.require]
将INVOKE(f,t1,t2,...,tN,R)定义为隐式转换为R的INVOKE(f,t1,t2,...,tN).
总之,INVOKE(f, t1, t2, ..., tN, R)当是永远有效R的void,如无(含void)可以隐式转换为void.
因此,所有都std::function<void(Args...)>具有属性!*this,因此不能被调用,因为唯一没有!*this后置条件的构造函数(或者不从function相同类型的另一个复制此类状态)需要Callable其中一个参数.
20.9.11.2/7类模板函数[func.wrap.func]
要求:F应为CopyConstructible.对于参数类型ArgTypes,f应为Callable(20.9.11.2)并返回R类型.A的拷贝构造函数和析构函数不应抛出异常.
20.9.11.2/2类模板函数[func.wrap.func]
类型F的可调用对象f对于参数类型ArgTypes是Callable,如果表达式INVOKE(f,declval()...,R)被认为是未评估的操作数(第5条),则返回类型R( 20.9.2).
如上所述,没有Callable表达std::function<void(Args...)>.
如果以某种方式std::function<void(Args...)>找到这样的地方,调用operator()将会形成错误:
调用[func.wrap.func.inv]
效果:INVOKE(f,std :: forward(args)...,R)(20.9.2),其中f是*this的目标对象(20.9.1).
因为INVOKE(f, …
请考虑以下代码段:
#include <iostream>
#include <vector>
#include <functional>
int main()
{
std::vector<int>v = {0,1,2,3,4,5,6};
std::function<const int&(int)> f = [&v](int i) { return v[i];};
std::function<const int&(int)> g = [&v](int i) -> const int& { return v[i];};
std::cout << f(3) << ' ' << g(3) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我期待相同的结果:in f,v由const引用传递,所以v[i]应该有const int&类型.
但是,我得到了结果
0 3
Run Code Online (Sandbox Code Playgroud)
如果我不使用std :: function,一切都很好:
#include <iostream>
#include <vector>
#include <functional>
int main()
{
std::vector<int>v = {0,1,2,3,4,5,6};
auto f = [&v](int i) …Run Code Online (Sandbox Code Playgroud) 已经提出了C++"委托"的建议,其开销低于boost::function:
是否已将这些想法用于实施std::function,从而产生更好的性能boost::function?有谁比较过std::functionvs 的表现boost::function?
我想知道这个特别适用于英特尔64位架构上的GCC编译器和libstdc ++,但欢迎其他编译器的信息(例如Clang).
在下面的玩具示例中,我想获得一个函数的名称.函数本身作为std::function参数给出.在C++中是否可以获取std::function对象的名称?
void printName(std::function<void()> func){
//Need a function name()
std::cout << func.name();
}
void magic(){};
//somewhere in the code
printName(magic());
output: magic
Run Code Online (Sandbox Code Playgroud)
否则我必须将函数的名称作为第二个参数.
采取以下最小例子:
using Type1 = std::function<void(void)>;
template <typename T>
using Type2 = std::function<void(T)>;
Type1 whyDoesThisWork;
Type2<void> andYetThisDoesNot;
Run Code Online (Sandbox Code Playgroud)
如果第二个类型别名,我得到错误"参数可能没有'void'类型".(我使用Xcode 4.5,Clang/c ++ 11/libc ++,OS X 10.7进行了测试.)
我发现这很奇怪:我本来期望Type1并且Type2<void>行为相同.这里发生了什么?有没有办法重写第二类型别名,所以我可以编写Type2<void>并获取std::function<void(void)>而不是错误?
编辑我应该补充一点,我想要的原因是允许以下内容:
template <typename ... T>
using Continuation = std::function<void(T...)>;
auto someFunc = []() -> void {
printf("I'm returning void!\n");
};
Continuation<decltype(someFunc())> c;
Run Code Online (Sandbox Code Playgroud)
Continuation<decltype(someFunc())>成为Continuation<void>,我得到了错误.
基本上,我想有以下语义:
#include <functional>
#include <iostream>
class test
{
public:
void add(std::function<void()> f)
{
f();
}
void operator()()
{
++x;
}
int x = 33;
};
int main()
{
test t;
t.add(t);
// wanted: x == 34 instead: x == 33 since add(t) copies it
}
Run Code Online (Sandbox Code Playgroud)
我理解std :: function包装了一个可调用对象的副本,但有没有办法使用std :: function获取对可调用对象的引用?
前一段时间我用std::function的很像:
std::function<void(int)> func = [](int i) -> int { return i; };
Run Code Online (Sandbox Code Playgroud)
基本上,我这样做是因为我想在a中存储不同的函数对象std::function,但我不想限制这些函数的返回类型.由于这似乎有效,我接受了它.但我不相信它是安全的,我也无法找到任何文件.有谁知道这种用法是否合法?或者更一般地说,对象的规则是什么,可以安全地分配给std::function?
为了澄清,我关心的问题是lambda函数返回一个int,而func声明返回类型void.我不确定这是否正常,特别是一旦打电话func().
c++ ×10
std-function ×10
c++11 ×9
lambda ×3
boost ×1
c++14 ×1
c++20 ×1
clang ×1
libc++ ×1
performance ×1
return-type ×1
templates ×1