为什么我们不能轻易复制std :: function

时间:2016-12-20 17:09:02

标签: c++ c++11 std-function

我要问这个的原因是我需要将memcpy存储在一个向量中,而我们公司内部的向量基本上是在需要更多内存的情况下进行重新分配。 (基本上只是void* func1Buffer = malloc(sizeof(std::function<void(int)>)); std::function<void(int)>* func1p = new (func1Buffer) std::function<void(int)>(); std::function<void(int)>* func2p = nullptr; *func1p = [](int) {}; char func2Buffer[sizeof(*func1p)]; memcpy(&func2Buffer, func1p, sizeof(*func1p)); func2p = (std::function<void(int)>*)(func2Buffer); // func2p is still valid here (*func2p)(10); free(func1Buffer); // func2p is now invalid, even without std::function<void(int)> desctructor get triggered (*func2p)(10); ,没有复制/移动操作符)

这意味着我们可以放入容器中的所有元素都需要可以轻易复制。

以下是一些代码,用于演示我遇到的有问题的副本:

std::function

我知道我们应该支持元素的复制/移动,以便安全地存储std::function。 但我仍然非常好奇上面无效std::function副本的直接原因是什么。

-------------------------------------------- -------- UpdateLine ----------------------------------------- -----------

更新了代码示例。

通过调试我们的内部矢量,我发现了这种失败的直接原因。

平凡复制的std::function对原始对象内存有一定的依赖性,删除原始内存将会删除严重复制的class Countries: def __init__(self): self.__countries = ['afghanistan', 'aland islands', 'albania', 'algeria', 'american samoa', 'andorra', 'angola', 'anguilla', 'antarctica', 'antigua and barbuda', 'argentina', 'armenia', 'aruba', 'australia', 'austria', 'azerbaijan', 'bahamas (the)', 'bahrain', 'bangladesh', 'barbados', 'belarus', 'belgium', 'belize', 'benin', 'bermuda', 'bhutan', 'bolivia (plurinational state of)', 'bonaire, sint eustatius and saba', 'bosnia and herzegovina', 'botswana', 'bouvet island', 'brazil', 'british indian ocean territory (the)', 'brunei darussalam', 'bulgaria', 'burkina faso', 'burundi', 'cabo verde', 'cambodia', 'cameroon', 'canada', 'cayman islands (the)', 'central african republic (the)', 'chad', 'chile', 'china', 'christmas island', 'cocos (keeling) islands (the)', 'colombia', 'comoros (the)', 'congo (the democratic republic of the)', 'congo (the)', 'cook islands (the)', 'costa rica', "cote d'ivoire", 'croatia', 'cuba', 'curacao', 'cyprus', 'czechia', 'denmark', 'djibouti', 'dominica', 'dominican republic (the)', 'ecuador', 'egypt', 'el salvador', 'equatorial guinea', 'eritrea', 'estonia', 'ethiopia', 'falkland islands (the) [malvinas]', 'faroe islands (the)', 'fiji', 'finland', 'france', 'french guiana', 'french polynesia', 'french southern territories (the)', 'gabon', 'gambia (the)', 'georgia', 'germany', 'ghana', 'gibraltar', 'greece', 'greenland', 'grenada', 'guadeloupe', 'guam', 'guatemala', 'guernsey', 'guinea', 'guinea-bissau', 'guyana', 'haiti', 'heard island and mcdonald islands', 'holy see (the)', 'honduras', 'hong kong', 'hungary', 'iceland', 'india', 'indonesia', 'iran (islamic republic of)', 'iraq', 'ireland', 'isle of man', 'israel', 'italy', 'jamaica', 'japan', 'jersey', 'jordan', 'kazakhstan', 'kenya', 'kiribati', "korea (the democratic people's republic of)", 'korea (the republic of)', 'kuwait', 'kyrgyzstan', "lao people's democratic republic (the)", 'latvia', 'lebanon', 'lesotho', 'liberia', 'libya', 'liechtenstein', 'lithuania', 'luxembourg', 'macao', 'macedonia (the former yugoslav republic of)', 'madagascar', 'malawi', 'malaysia', 'maldives', 'mali', 'malta', 'marshall islands (the)', 'martinique', 'mauritania', 'mauritius', 'mayotte', 'mexico', 'micronesia (federated states of)', 'moldova (the republic of)', 'monaco', 'mongolia', 'montenegro', 'montserrat', 'morocco', 'mozambique', 'myanmar', 'namibia', 'nauru', 'nepal', 'netherlands (the)', 'new caledonia', 'new zealand', 'nicaragua', 'niger (the)', 'nigeria', 'niue', 'norfolk island', 'northern mariana islands (the)', 'norway', 'oman', 'pakistan', 'palau', 'palestine, state of', 'panama', 'papua new guinea', 'paraguay', 'peru', 'philippines (the)', 'pitcairn', 'poland', 'portugal', 'puerto rico', 'qatar', 'reunion', 'romania', 'russian federation (the)', 'rwanda', 'saint barthelemy', 'saint helena, ascension and tristan da cunha', 'saint kitts and nevis', 'saint lucia', 'saint martin (french part)', 'saint pierre and miquelon', 'saint vincent and the grenadines', 'samoa', 'san marino', 'sao tome and principe', 'saudi arabia', 'senegal', 'serbia', 'seychelles', 'sierra leone', 'singapore', 'sint maarten (dutch part)', 'slovakia', 'slovenia', 'solomon islands', 'somalia', 'south africa', 'south georgia and the south sandwich islands', 'south sudan', 'spain', 'sri lanka', 'sudan (the)', 'suriname', 'svalbard and jan mayen', 'swaziland', 'sweden', 'switzerland', 'syrian arab republic', 'taiwan (province of china)', 'tajikistan', 'tanzania, united republic of', 'thailand', 'timor-leste', 'togo', 'tokelau', 'tonga', 'trinidad and tobago', 'tunisia', 'turkey', 'turkmenistan', 'turks and caicos islands (the)', 'tuvalu', 'uganda', 'ukraine', 'united arab emirates (the)', 'united kingdom of great britain and northern ireland (the)', 'united states minor outlying islands (the)', 'united states of america (the)', 'uruguay', 'uzbekistan', 'vanuatu', 'venezuela (bolivarian republic of)', 'viet nam', 'virgin islands (british)', 'virgin islands (u.s.)', 'wallis and futuna', 'western sahara*', 'yemen', 'zambia', 'zimbabwe'] def __call__(self, name, strict=3): result = False name = name.lower() if strict==3: for country in self.__countries: if country==name: return True else: return result elif strict==2: for country in self.__countries: if name in country: return True else: return result elif strict==1: for country in self.__countries: if country.startswith(name): return True else: return result else: return result countries = Countries() print (countries('germany')) print (countries('russia')) print (countries('russia', strict=2)) print (countries('russia', strict=1)) print (countries('samoa', strict=2)) print (countries('samoa', strict=1)) 甚至,而不会破坏原始对象。< / p>

感谢大家对这篇文章的回答。这都是有价值的投入。 :)

4 个答案:

答案 0 :(得分:7)

问题在于std::function必须如何实施:它必须管理它所持有的任何对象的生命周期。所以当你写:

{
    std::function<Sig> f = X{};
} 

X超出范围时,我们必须调用f的析构函数。此外,std::function将[可能]分配内存以保存X,因此f的析构函数也必须[可能]释放该内存。

现在考虑当我们尝试时会发生什么:

char buffer[100000]; // something big
{
    std::function<void()> f = X{};
    memcpy(buffer, &f, sizeof(f));
}
(*reinterpret_cast<std::function<void()>*>(buffer))();

我们正在调用函数&#34;存储&#34;在bufferX对象已被破坏,并且[可能]释放了存放它的内存。无论XTriviallyCopyable,我们都不再拥有X。我们这位艺术家以前称为X

因为std::function有责任管理自己的对象,所以即使我们添加了它管理的所有可调用对象的要求TriviallyCopyable ,也不能TriviallyCopyable {1}}。

要在realloc_vector中工作,您需要使用类似function_ref(或std::function<>*)的内容(即一种根本不拥有任何资源的类型),或者您需要实现自己的function版本,以便(a)将自己的存储保留为成员以避免分配内存(b)只能使用TriviallyCopyable callables构建这样它本身就变得可以轻易复制了。哪种解决方案更好取决于您的程序实际执行的操作。

答案 1 :(得分:4)

  

但我仍然非常好奇无效的直接原因是什么   上面的std :: function copy。

std::function不能是TriviallyCopyable(或有条件TriviallyCopyable),因为作为通用可调用对象包装器,它不能假设存储的可调用对象是TriviallyCopyable

考虑实现您自己的std::function版本,该版本仅支持TriviallyCopyable可调用对象(使用固定缓冲区进行存储),或者在您的情况下使用函数指针向量。

答案 2 :(得分:3)

简单地可复制是与给定类型本质上相关的东西,而不是与对象相关的东西 请考虑以下示例:

import {isString} from '../util/util';

一旦你将lambda分配给函数,你怎么能强制执行这个属性,这不是一个简单的可复制的?

您正在寻找的是运行时机制,它根据分配给该功能的实际对象做出决定 这不是#include<type_traits> #include<functional> int main() { auto l = [](){}; static_assert(not std::is_trivially_copyable<decltype(l)>::value, "!"); std::function<void(void)> f; bool copyable = std::is_trivially_copyable<decltype(f)>::value; f = l; // do something based on the // fact that f is trivially copyable } 的工作原理 因此,编译器必须在编译时就std::is_trivially_copyable的给定特化进行决策。因为它是可调用对象的通用容器,你可以为它分配简单的可复制对象以及不易复制的对象,其余部分不言而喻。

答案 3 :(得分:2)

std :: function可能会为捕获的变量分配内存。与任何其他分配内存的类一样,它不是可以轻易复制的。