C++ 如何测试琐碎的复制可分配lambda
如果有人能给我一个提示,告诉我如何测试函子的简单复制能力,我将不胜感激。正如在中所解释的,实现定义了lambda是否是可复制的。例如,对于本问题末尾所示的代码,gcc(5.4)和msvc(2015)都提出了这样的断言,即它们不是简单的复制可分配的 我希望这些类型的lambda由C++ 如何测试琐碎的复制可分配lambda,c++,c++11,lambda,C++,C++11,Lambda,如果有人能给我一个提示,告诉我如何测试函子的简单复制能力,我将不胜感激。正如在中所解释的,实现定义了lambda是否是可复制的。例如,对于本问题末尾所示的代码,gcc(5.4)和msvc(2015)都提出了这样的断言,即它们不是简单的复制可分配的 我希望这些类型的lambda由struct表示,保留this指针,并拥有每个捕获值(如果有)的副本。所以它们看起来都是可复制的——至少在捕获的值可复制的情况下是如此 我提出这个问题的真正用例是,我正在使用一个回调(不分配的std::function的最
struct
表示,保留this
指针,并拥有每个捕获值(如果有)的副本。所以它们看起来都是可复制的——至少在捕获的值可复制的情况下是如此
我提出这个问题的真正用例是,我正在使用一个回调(不分配的std::function
的最低版本,用于非常简单的函子),它保留一个固定的缓冲区,在其中复制(就地)传递的函子的结构。然后,我希望能够复制/分配这些回调,但为了实现这一点(开箱即用),这些固定缓冲区的简单mem复制应该相当于保留在其中的函子的处理/分配
所以我有两个问题:
test\u functor()
中,我做了new
放置,例如
new(&buffer)F(functor)
对于下面所示的lambda类型,只使用memcopy
这个缓冲区安全吗?我认为应该是这样的,因为在所有情况下,只捕获了这个指针,或者捕获的值都是可复制的,但是如果有人能够确认这一点,那就太好了
std::就不是正确的答案
#包括
模板
无效测试函数(常数F&函数)
{
静态断言(std::是可破坏的::值,
“不可平凡破坏的函子”);
静态断言(std::是可构造的::值,
“函子不容易复制可构造”);
静态断言(std::is_琐碎地\u复制\u可赋值::值,
“函子不平凡地复制可赋值”);
}
结构A
{
void test(){test_函子([this](){});}
};
结构B
{
void test(){test_函子([this](int v){value=v;});}
int值;
};
结构C
{
无效测试(intv){test_函子([=](){value=v;});}
int值;
};
int main()
{
A A;
B B;
C C;
a、 test();
b、 test();
c、 试验(1);
返回0;
}
不,这不安全。如果编译器说某些东西不能被简单地复制,那么它就不能被复制
它可能会起作用。但这并不意味着它是安全的
即使它今天可以工作,但明天在编译器更新一段时间后就停止工作了
解决方法非常简单。编写一个SBO类型(小缓冲区优化),它不需要简单的可复制性
template<std::size_t S, std::size_t A>
struct SBO {
void(*destroy)(SBO*) = nullptr;
// void(*copy_ctor)(SBO const* src, SBO* dest) = nullptr;
void(*move_ctor)(SBO* src, SBO* dest) = nullptr;
std::aligned_storage_t< S, A > buffer;
void clear() {
auto d = destroy;
destroy = nullptr;
// copy_ctor = nullptr;
move_ctor = nullptr;
if (d) d(this);
}
template<class T, class...Args>
T* emplace( Args&&... args ) {
static_assert( sizeof(T) <= S && alignof(T) <= A, "not enough space or alignment" );
T* r = new( (void*)&buffer ) T(std::forward<Args>(args)...);
destroy = [](SBO* buffer) {
((T*)&buffer->buffer)->~T();
};
// do you need a copy ctor? If not, don't include this:
//copy_ctor = [](SBO const* src, SBO* dest) {
// auto s = (T const*)&src.buffer;
// dest->clear();
// dest->emplace<T>( *s );
//};
move_ctor = [](SBO* src, SBO* dest) {
auto* s = (T*)&src->buffer;
dest->clear();
dest->emplace<T>( std::move(*s) );
src->clear();
};
return r;
}
SBO() = default;
SBO(SBO&& o) {
if (o.move_ctor) {
o.move_ctor(&o, this);
}
}
SBO& operator=(SBO&& o) {
if (this == &o) return *this; // self assign clear, which seems surprising
if (o.move_ctor) {
o.move_ctor(&o, this);
}
return *this;
}
// do you need a copy ctor? If so, implement `SBO const&` ctor/assign
};
模板
结构SBO{
无效(*销毁)(SBO*)=无效PTR;
//无效(*副本)(SBO const*src,SBO*dest)=nullptr;
无效(*移动系数)(SBO*src,SBO*dest)=空PTR;
std::对齐存储\u t缓冲区;
无效清除(){
自动d=销毁;
销毁=空PTR;
//复制系数=nullptr;
move_ctor=nullptr;
若(d)d(本);
}
模板
T*定位(Args&&…Args){
您应该使用的静态断言(sizeof(T)是可复制的,而不是可构造的或可分配的。lambda从来都不是可复制的(或移动的)因为他们的复制赋值操作符被删除了,所以是可赋值的;但这并不一定会阻止他们的可复制性。@t.C.-你能在“答案”部分发表你的评论吗?我想把它标记为答案,因为你一针见血。我不知道lambdas的复制赋值被删除了。事实上,是不可复制的pyable
正在通过。谢谢你。谢谢你的例子。我会看一下,但同时我会保留它们(我在测试std::function
——我想是用GCC4.9——它是为我正在使用的lambdas分配的)。请看T.C的评论——这与你的第一句话非常吻合:)-我应该通过std::is\u littlely\u copyable
测试琐碎的可复制性regards@AndrzejO笑!我错过了。我觉得编译器有点傻。不过,如果它是个傻瓜,你不会说“我真的知道发生了什么”雅克,我决定把你的答案标记为一个解决方案,因为MSVC似乎仍然声称这样的lambda不是简单的可复制的。我打赌编译器会出错,但在这种情况下,我别无选择,只能自己复制。
template<std::size_t S, std::size_t A>
struct SBO {
void(*destroy)(SBO*) = nullptr;
// void(*copy_ctor)(SBO const* src, SBO* dest) = nullptr;
void(*move_ctor)(SBO* src, SBO* dest) = nullptr;
std::aligned_storage_t< S, A > buffer;
void clear() {
auto d = destroy;
destroy = nullptr;
// copy_ctor = nullptr;
move_ctor = nullptr;
if (d) d(this);
}
template<class T, class...Args>
T* emplace( Args&&... args ) {
static_assert( sizeof(T) <= S && alignof(T) <= A, "not enough space or alignment" );
T* r = new( (void*)&buffer ) T(std::forward<Args>(args)...);
destroy = [](SBO* buffer) {
((T*)&buffer->buffer)->~T();
};
// do you need a copy ctor? If not, don't include this:
//copy_ctor = [](SBO const* src, SBO* dest) {
// auto s = (T const*)&src.buffer;
// dest->clear();
// dest->emplace<T>( *s );
//};
move_ctor = [](SBO* src, SBO* dest) {
auto* s = (T*)&src->buffer;
dest->clear();
dest->emplace<T>( std::move(*s) );
src->clear();
};
return r;
}
SBO() = default;
SBO(SBO&& o) {
if (o.move_ctor) {
o.move_ctor(&o, this);
}
}
SBO& operator=(SBO&& o) {
if (this == &o) return *this; // self assign clear, which seems surprising
if (o.move_ctor) {
o.move_ctor(&o, this);
}
return *this;
}
// do you need a copy ctor? If so, implement `SBO const&` ctor/assign
};