C++ C++;变换副作用

C++ C++;变换副作用,c++,stl,C++,Stl,我已经实现了这样的一个操作 struct Converter { Converter( std::size_t value ): value_( value ), i_( 0 ) {} std::string operator() ( const std::string& word ) { return ( value_ & ( 1 << i_++ ) ) ? word:

我已经实现了这样的一个操作

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_;
};
我的问题是,我能否依赖我的假设,即算法将按照“Converter::I_”对应于“v”中元素数量的顺序调用我的“Converter operator()”

请引用标准,以防我不能依赖订单,或提出类似stl的解决方案,以避免可能出现的问题(如果有的话)

谢谢

编辑:

我知道转换算法标准中的“无副作用”要求。在同一标准中,我找不到函子的确切“副作用”


可能有一些好看的类似于boost的解决方案可用于此任务?

是和否。这是因为您将迭代器提供给了
转换器
对象。对于像
vector
这样的序列容器,您可以得到
i
来对应元素的顺序。但是,对于像
map/multimap/set/multiset
这样的关联容器,这可能无效(很可能无效)

我刚刚检查了标准,如果我理解正确,答案是否定的。“转换”的描述有以下附加要求(25.2.3):

要求:op和binary_op不得有任何副作用

回到我的记忆深处,我记得在一次会议上的一次演讲,他展示了对于特定类型的容器和STL实现,函数对象是被复制的。提供了Dobb博士文章的链接,该文章更详细地讨论了这些差异

编辑:替代解决方案:

for_each算法没有此限制,因此您可以更改转换器对象以引用结果向量,并在转换器函数中执行推回操作

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_;
};
来自标准的Qute:

25.2.3转换[lib.alg.Transform]
需要:
op和binary_op不得有任何副作用

在您的情况下,我们有另一个副作用:

Converter c( data );  
c( some_const_value ) != c( some_const_value );
您没有任何算法保证,但我相信它几乎可以在所有stl实现上运行

建议的解决方案
看来我知道一种方法可以满足你的需要:
使用boost::counting_迭代器-迭代两个容器

它看起来像:

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 )
);
bool位已启用(大小数据,无符号字符数)
{
返回(数据和1词汇={“a”、“b”、“c”};
std::vector结果;
std::transform(
词汇表。开始(),
词汇表.end(),
boost::计数迭代器(0),
背面插入器(结果),
boost::bind(选择\u字、\u 1、数据、\u 2(&S)
);
另外,如果您将定义位迭代器或使用某个位容器,则可以使用boost::zip_迭代器来迭代这两个容器

编辑:
是的,我找到了包含副作用定义的标准

该标准将副作用定义为 如下:访问对象 由易变左值指定, 修改对象,调用库 I/O函数,或调用函数 也就是说,这些操作中的任何一个都是 所有的副作用,即 执行情况 环境

编辑:
我希望它是最新的编辑。
我一直认为“没有副作用”的意思是:
f(a)应始终等于f(a)。(f与执行环境无关:内存/cpu/全局变量/成员变量,如您的情况等)。
“不产生副作用”是指不改变执行环境

<>但在C++标准中,副作用的定义较低,

您在示例中所做的命名为有状态函子的操作。
标准中没有提到“Statefull”functor,但也没有提到您的functor的副本数-您不能使用此技巧,因为它是未指定的行为

请参阅标准库问题列表(谓词的类似问题):

我相信在某些情况下答案是肯定的,但也有肯定的情况是否定的。如果无法回忆起是哪种情况(对不起),并且为了使代码不那么脆弱,我不会依赖它,也不会为了安全而将我放在函子之外。 ie内部
转换器
do:


转换器(std::size\u t value,std::size\u t&i):值(value),i(i){


std::size\u t&i;


然后调用

std::vector v;

std::size\u ti(0);

//v的初始化

std::transform(v.begin(),v.end(),
std::back_插入器(结果)、转换器(数据、i));


Messy,但更安全。

< P>作为副作用肯定是坏事的一个例子,考虑一个假设的并行STL实现,它在几个CPU之间分割工作。


我相信这是STL作者的想法。原始STL来自SGI,SGI是构建大规模并行单映像和集群系统的大牌之一。

set算法仍然使用输出迭代器添加新项。因此需要一个迭代器并将其推送到输出。我怀疑“一元操作不会有任何副作用”。感谢您对“副作用”的解释。很好的计数迭代器!转换可以使用两个容器。您能再次更改解决方案吗?我也考虑过副作用的定义。但在这种情况下它没有任何意义。假设您在操作符()中执行此操作:{int a;a=4;return T(T,a);}将是副作用(修改局部变量“a”),因此转换规范不允许。最后,我认为它想说的是,它不能依赖文章中描述的任何成员/自由变量。很好的boost解决方案。很好的链接。感谢对副作用的解释。”在实践中,我
Converter c( data );  
c( some_const_value ) != c( some_const_value );
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 )
);