C++ std::transform 副作用
我已经实现了这样的 UnaryOperation
struct Converter
{
Converter( std::size_t value ):
value_( value ), i_( 0 )
{}
std::string operator() ( const std::string& word )
{
return ( value_ & ( 1 << i_++ ) ) ?
word:
std::string( word.size(), ' ' );
}
std::size_t value_;
std::size_t i_;
};
我像这样使用它
std::vector v;
// initialization of v
std::transform( v.begin(), v.end(),
std::back_inserter( result ),
Converter( data ) );
我的问题是我可以依赖我的假设,即算法将按照“Converter::i_”的顺序调用我的“Converter operator ()”将对应于“v”中的元素数量。
请引用标准,以防我不能依赖订单或提出类似 stl 的解决方案,以避免可能出现的问题(如果有)。
谢谢。
编辑:
我知道变换算法标准中的“无副作用”要求。 我找不到同一标准中函子的确切“副作用”。
也许有一些看起来不错的类似Boost的解决方案来完成这个任务?
I've implementation of UnaryOperation like this
struct Converter
{
Converter( std::size_t value ):
value_( value ), i_( 0 )
{}
std::string operator() ( const std::string& word )
{
return ( value_ & ( 1 << i_++ ) ) ?
word:
std::string( word.size(), ' ' );
}
std::size_t value_;
std::size_t i_;
};
And I use it like
std::vector v;
// initialization of v
std::transform( v.begin(), v.end(),
std::back_inserter( result ),
Converter( data ) );
My question is can I rely on my assumption that algorithm will call my 'Converter operator ()' in the order that 'Converter::i_' will correspond to number of element in 'v'.
Please quote the standard in case I can't rely on the order or put the stl-like solution that avoid possible problem if any.
Thanks.
Edit:
I am aware of "no Side effect" requirements in the standard for the transform algorithm. I can't find what is exactly "side effect" for functors in the same standard.
Maybe there is some good-looking-boost-like solution for this task?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
从标准来看:
副作用(维基百科定义)
在您的情况下,我们有下一个副作用:
您不'对你的算法没有任何保证,但我相信它适用于几乎所有的 stl 实现。
建议的解决方案
看来我知道一种方法可以满足您的需求:
使用 boost::counting_iterator - 用于迭代两个容器;
它看起来像:
另外,如果您将定义位迭代器或将使用一些位容器,您将可以使用 boost::zip_iterator 来迭代这两个容器。
编辑:
昨天我发现感兴趣的文章其中包含标准的副作用定义。
编辑:
我希望这是最新的编辑。
我一直认为“没有副作用”的意思是:
f(a) 应始终等于 f(a)。 ( f 独立于执行环境:内存/cpu/全局变量/成员变量,如您的情况等)。
“不产生副作用”的意思是——不改变执行环境。
但在 C++ 标准中,我们对副作用有更底层的定义。
将您在示例中所做的事情命名为 Stateful 函子。
标准没有提到“Statefull”函子,也没有提到函子的副本数量 - 你不能使用这个技巧,因为它是未指定的行为。
请参阅标准库问题列表(谓词的类似问题):
http://anubis.dkuug.dk/jtc1/sc22 /wg21/docs/lwg-active.html#92
Qute from standard:
Side Effect ( wikipedia definition )
In your case we have next side effect:
You don't have any guarantees for your algorithms, but I belive that it will works on almost all stl implementations.
Suggested solution
It seems I know one way to do what you need:
use boost::counting_iterator - for iterate over two containers;
it will looks like:
Also maybe if you will define bit iterator or will use some bit container you will can use boost::zip_iterator for iterate both containers.
EDIT:
Yestarday I found interest article which contain definition of Side Effect by standard.
EDIT:
I hope it will be latest edit.
I am always tought that "no have side effect" mean:
f(a) should be equal f(a) always. ( f independed from execution environment: memory/cpu/global variables/member variables as in your case etc).
"Not produce side effect" mean - don't changing execution environment.
But in c++ standard we have more low-level defintion for Side effect.
Thing what you do in your example named as Stateful functor.
Standard doesn't say about "Statefull" functors, but also doesn't say about count of copies of your functor - you couldn't use this trick because it is unspecified behavior.
See Standard Library Issues list ( similar issue for predicat ):
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-active.html#92
我刚刚检查了标准,如果我理解正确的话,答案是否定的。 “transform”的描述有以下附加要求(25.2.3):
回到我的记忆深处,我记得 Nicolai Josuttis 在 ACCU 会议,他在会上展示了对于特定类型的容器和 STL 实现,函数对象被复制。 埃里克提供了此链接指向 Dobb 博士的文章,该文章更详细地讨论了这些差异。
编辑:替代解决方案:
for_each算法没有此限制,因此您可以更改 Converter 对象以引用结果向量并在 Converter 内执行push_back功能。
并使用for_each而不是transform:
I've just checked the standard and if I understand it correctly the answer is no. The description of 'transform' has the following additional requirement (25.2.3):
Reaching back into the depths of my memory, I remember a talk given Nicolai Josuttis at an ACCU conference, where he showed that for a particular type of container and STL implementation, the function object was copied. Éric provided this link to a Dr. Dobb's article that discusses the differences in more detail.
EDIT: Alternative solution:
The for_each algorithm does not have this restriction, so you could change your Converter object to take a reference to the result vector and do the push_back inside the Converter function.
And use for_each rather than transform:
作为副作用肯定是一件坏事的例子,请考虑一个假设的并行 STL 实现,它将工作分配给多个 CPU。
我相信这是STL的作者们的想法。 最初的 STL 来自 SGI,它是构建大规模并行单图像和集群系统的知名公司之一。
As an example of a case where side-effects would be a definite bad thing, consider a hypothetical parallel STL implementation which split the work between several CPUs.
I believe this was in the minds of the authors of STL. The original STL was from SGI, one of the larger names in building massively parallel single-image and cluster systems.
是和不是。 这是因为您将迭代器提供给
Converter
对象。 对于像vector
这样的序列容器,您可以得到i
来对应元素的顺序。 但是,对于像map/multimap/set/multiset
这样的关联容器,这可能无效(很可能无效)。Yes and no. This is because you feed iterators to your
Converter
object. For a sequence container likevector
you geti
to correspond to the order of the elements. However, for associative containers likemap/multimap/set/multiset
this may not be (will most probably not be) valid.我相信在某些情况下答案是肯定的,但也有明确的情况是否定的。 如果无法回忆起哪个(抱歉),并且为了使代码不那么脆弱,我不会依赖它并将 i 保留在仿函数之外以确保安全。
即在
Converter
中执行以下操作:<代码>
转换器( std::size_t value, std::size_t& i ): value_( value ), i_ ( i ) {}
和
std::size_t &i_;
然后调用
std::向量 v;
std::size_t i(0);
// v 的初始化
std::transform( v.begin(), v.end(),
std::back_inserter( result ), Converter( data, i ) );
凌乱,但更安全。
I believe there are some instances where the answer is yes, but there are also definite cases where it's no. Without being able to recall which (sorry), and to make the code less brittle, I would not rely on it and keep i outside the functor to be safe.
ie inside
Converter
do:Converter( std::size_t value, std::size_t& i ): value_( value ), i_ ( i ) {}
and
std::size_t &i_;
then invoke with
std::vector v;
std::size_t i(0);
// initialization of v
std::transform( v.begin(), v.end(),
std::back_inserter( result ), Converter( data, i ) );
Messy, but safer.