C++ std::generate#n'安全吗;s functor捕获并操作容器it';它是用来填充的吗?

C++ std::generate#n'安全吗;s functor捕获并操作容器it';它是用来填充的吗?,c++,language-lawyer,C++,Language Lawyer,最近,我一直在玩STL算法和lambdas,以实现不那么琐碎的功能。几周前,我被告知谓词(对于exmaple-在std::count\u if中使用的lambda)修改传递给它的值是非法的 我只是接受了这一点,并记住永远不要尝试做那样的事情。然而,我刚刚遇到了一个潜在的类似情况,我不确定这样使用代码是否合法: auto generate_n_fib(const std::size_t count = 2) { assert(count != 0); if(count == 1)

最近,我一直在玩STL算法和lambdas,以实现不那么琐碎的功能。几周前,我被告知谓词(对于exmaple-在
std::count\u if
中使用的lambda)修改传递给它的值是非法的

我只是接受了这一点,并记住永远不要尝试做那样的事情。然而,我刚刚遇到了一个潜在的类似情况,我不确定这样使用代码是否合法:

auto generate_n_fib(const std::size_t count = 2) {
    assert(count != 0);
    if(count == 1) {
        return std::vector<std::size_t>{0};
    }
    if(count == 2) {
        return std::vector<std::size_t>{0, 1};
    }
    std::vector<std::size_t> fib{};
    fib.emplace_back(0);
    fib.emplace_back(1);

    if(count > 2) {
        std::generate_n(std::back_inserter(fib), count - 2, [&fib](){
            const auto last = fib.size() - 1;
            return fib[last] + fib[last - 1];
        });
    }

    return fib;
}
auto-generate\n\u fib(const std::size\t count=2){
断言(计数!=0);
如果(计数=1){
返回std::向量{0};
}
如果(计数=2){
返回std::向量{0,1};
}
std::向量fib{};
fib.后侵位(0);
fib.后侵位(1);
如果(计数>2){
std::generate_n(std::back_inserter(fib),count-2,[&fib](){
const auto last=fib.size()-1;
返回fib[last]+fib[last-1];
});
}
返回fib;
}
如您所见,上面的函数根据斐波那契序列生成第一个
count
数字。相关部分是
std::generate\n
调用


我的问题是-生成器函数访问由使用该生成器函数的算法修改的集合的字段是否安全

一种可能的实现方式是:

被命名的lambda是
g

auto g = [&fib]() {
    const auto last = fib.size() - 1;
    return fib[last] + fib[last - 1];
};
这是很明确的


我们仍然可以展开操作;摆脱lambda,即反向插入器迭代器,完成for循环中的所有工作,同时拥有定义良好的代码。

的一个可能实现是:

被命名的lambda是
g

auto g = [&fib]() {
    const auto last = fib.size() - 1;
    return fib[last] + fib[last - 1];
};
这是很明确的


我们仍然可以展开操作;去掉lambda,即反向插入器迭代器,完成for循环中的所有工作,同时拥有定义良好的代码。

本问题的最后一段询问的是一般的生成器函数,而不是特定的生成器函数。显示的生成器函数格式良好。但不难想到一个反例,它涉及一个生成器函数,该函数捕获其容器,然后以一种导致未定义行为的方式执行某些操作。@SamVarshavchik这是一个很好的观点。也许我应该把这个问题分成生成器函数,它们只访问容器,而非访问容器。
const
-ness不一定是驱动因素。比如说,如果生成器按值捕获
vec.begin()
,并且只进行解引用,那么它仍然可能是未定义的行为。鉴于此生成器有效地
push_back()
,它将使所有现有迭代器无效,因此使用捕获的
begin()
迭代器将是未定义的行为。但是用一个提前调整大小的向量做同样的事情,用一个简单的前向迭代器遍历向量的现有内容,捕获
begin(),我认为有必要指出,使用
generate\n
实现斐波那契生成器并不一定是最好的方法。例如,添加捕获的变量来存储最后两个值,而不是从向量中读取,消除了关于如何指定generate_n工作的任何假设(因此,对于不太熟悉该语言的人来说,代码更容易理解),由于不再需要向量元素访问,因此可以提高效率。本问题的最后一段询问的是一般的生成器函数,而不是特定的生成器函数。显示的生成器函数格式良好。但不难想到一个反例,它涉及一个生成器函数,该函数捕获其容器,然后以一种导致未定义行为的方式执行某些操作。@SamVarshavchik这是一个很好的观点。也许我应该把这个问题分成生成器函数,它们只访问容器,而非访问容器。
const
-ness不一定是驱动因素。比如说,如果生成器按值捕获
vec.begin()
,并且只进行解引用,那么它仍然可能是未定义的行为。鉴于此生成器有效地
push_back()
,它将使所有现有迭代器无效,因此使用捕获的
begin()
迭代器将是未定义的行为。但是用一个提前调整大小的向量做同样的事情,用一个简单的前向迭代器遍历向量的现有内容,捕获
begin(),我认为有必要指出,使用
generate\n
实现斐波那契生成器并不一定是最好的方法。例如,添加捕获的变量来存储最后两个值,而不是从向量中读取,消除了关于如何指定generate_n工作的任何假设(因此,对于不太熟悉该语言的人来说,代码更容易理解),由于不再需要向量元素访问,因此可以提高效率。这只是一种可能的实现。@raket1111如果一个实现首先存储所有生成的值,然后将它们全部赋值,会怎么样?@为什么?这个实现违反了哪条规则?@xskxzr Oops,看起来你是对的。似乎没有违反这只是一个可能的实现。@Rakete1111如果一个实现首先存储所有生成的值,然后将它们全部赋值,会怎么样?@为什么?这个实现违反了哪条规则?@xskxzr Oops,看起来你是对的。似乎没有违反法律
auto g = [&fib]() {
    const auto last = fib.size() - 1;
    return fib[last] + fib[last - 1];
};