我实现了像这样的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_'对应'v'中元素的数量的顺序调用我的'Converter operator()'
如果我不能依赖订单或提出类似stl的解决方案以避免可能出现的问题,请引用该标准。
感谢。
修改:
我知道变换算法标准中的“无副作用”要求。我无法找到同一标准中的仿函数的“副作用”。
也许这个任务有一些好看的类似解决方案?
答案 0 :(得分:6)
来自标准的Qute:
25.2.3变换[lib.alg.transform]
要求:
op和binary_op不会有任何副作用。
Side Effect ( wikipedia definition )
在您的情况下,我们有下一个副作用:
Converter c( data );
c( some_const_value ) != c( some_const_value );
您对算法没有任何保证,但我相信它几乎适用于所有stl实现。
建议的解决方案
我似乎知道一种方法来做你需要的事情:
使用boost :: counting_iterator - 迭代两个容器;
它看起来像:
bool bit_enabled( size_t data, unsigned char number )
{
return ( data & 1 << number ) != 0;
}
std::string select_word(
const std::string& word,
size_t data,
size_t number )
{
return bit_enabled( data, number ) ? word : std::string( ' ', word.length() );
}
const size_t data = 7;
const boost::array< std::string, 3 > vocabulary = { "a", "b", "c" };
std::vector< std::string > result;
std::transform(
vocabulary.begin(),
vocabulary.end(),
boost::counting_iterator< size_t >(0),
back_inserter( result ),
boost::bind( &select_word, _1, data, _2 )
);
也许如果您将定义位迭代器或将使用一些位容器,您可以使用boost :: zip_iterator来迭代两个容器。
修改强>
Yestarday我发现interest article包含标准副作用的定义。
标准将副作用定义为 如下:访问对象 由易变的左值指定, 修改对象,调用库 I / O功能,或调用函数 做任何这些操作都是 所有副作用,都是变化的 执行的状态 环境。
修改强>
我希望这将是最新的编辑
我总是认为“没有副作用”的意思是:
f(a)应始终等于f(a)。 (f与执行环境无关:内存/ cpu /全局变量/成员变量,如你的情况等)
“不产生副作用”是指 - 不要改变执行环境。
但是在c ++标准中,我们对副作用有更多的低级别定义。
你在你的例子中做了什么,名为有状态仿函数 标准没有说“Statefull”仿函数,也没有说你的仿函数的副本数量 - 你不能使用这个技巧,因为它是未指明的行为。
请参阅标准库问题列表(类似于谓词的问题):
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-active.html#92
答案 1 :(得分:2)
我刚检查了标准,如果我理解正确,答案是否定的。 'transform'的描述有以下附加要求(25.2.3):
需要:op和binary_op不会产生任何副作用。
回到我的记忆深处,我记得在Nicolai Josuttis会议上给出ACCU的一个演讲,他在那里展示了对于特定类型的容器和STL实现,函数对象被复制了。 Éric为Dobb博士的文章提供了this链接,该文章更详细地讨论了这些差异。
编辑:替代解决方案:
for_each 算法没有此限制,因此您可以更改Converter对象以获取对结果向量的引用,并在Converter函数内执行push_back。
struct Converter
{
Converter( std::size_t value, std::vector<std::string> & result ):
value_( value ), i_( 0 ), result_(result)
{}
void operator() ( const std::string& word )
{
result_.push_back ( value_ & ( 1 << i_++ ) ) ?
word:
std::string( word.size(), ' ' );
}
std::size_t value_;
std::size_t i_;
std::vector<std::string> & result_;
};
并使用 for_each 而不是转换:
std::vector v;
// initialization of v
std::for_each ( v.begin()
, v.end(),
Converter( data, result ) );
答案 2 :(得分:1)
作为副作用是明确的坏事的情况的一个例子,考虑一个假设的并行STL实现,它将工作分成几个CPU。
我相信这是STL作者的想法。最初的STL来自SGI,SGI是建立大规模并行单图像和集群系统的大型公司之一。
答案 3 :(得分:0)
是和否。这是因为您将迭代器提供给Converter
对象。对于像vector
这样的序列容器,您可以i
对应元素的顺序。但是,对于像map/multimap/set/multiset
这样的关联容器,这可能不会(很可能不会)有效。
答案 4 :(得分:-1)
我相信在某些情况下,答案是肯定的,但也有一些确定的情况,即不是。如果不能回忆起哪个(抱歉),并且使代码不那么脆弱,我就不会依赖它并让我在仿函数之外保持安全。
即在Converter
内执行:
Converter( std::size_t value, std::size_t& i ): value_( value ), i_ ( i ) {}
和
std::size_t &i_;
然后用
调用
std::vector v;
std::size_t i(0);
// initialization of v
std::transform( v.begin(), v.end(),
std::back_inserter( result ), Converter( data, i ) );
凌乱,但更安全。