【问题标题】:Minimizing the scope of objects for a lambda expression最小化 lambda 表达式的对象范围
【发布时间】:2020-08-25 22:58:12
【问题描述】:

我想知道 C++ 中是否有一些我还不知道的工具可以在以下简单示例中最小化我的对象和函数的范围,其中我用随机骰子滚动填充向量:

std::vector<int> dice_samples(10);
std::mt19937_64 m_gen;
std::uniform_int_distribution<int> dist(1, 6);

std::generate(dice_sample.begin(), dice_sample.end(), [&](){
    return dist(m_gen);
    });

理想情况下,我希望生成器和分发都在生成调用范围内。我可以将它们的构造移到 lambda 表达式中,但这样的事情似乎效率极低,因为我将在每次调用时构造一个生成器和分布:

std::vector<int> dice_samples(10);

std::generate(dice_sample.begin(), dice_sample.end(), [](){
    std::mt19937_64 m_gen;
    std::uniform_int_distribution<int> dist(1, 6);
    return dist(m_gen);
    });

换句话说,有没有办法在函数调用或 lambda 表达式中确定对象的构造和销毁范围?还是在最新版本的 C++ 中还不能做到这一点?

【问题讨论】:

  • 你知道,我从未尝试在 lambda 中定义 static 变量。我想我有一些新的东西要查找。 How do static variables in lambda function objects work? 似乎回答了这个问题。看起来你可以使用static
  • @user4581301 我做了,但主要是作为一些计数器,而不是捕获外部计数器。可能会与通用 lambda 混淆.. 它们被视为模板
  • @Swift-FridayPie 你可能是对的。上面链接中的评论警告了这一点。不过,对于像这样的微不足道的情况,这应该不是问题。
  • @user4571391 有趣我以前也从未想过在 lambda 中使用静态。我想知道在这种情况下使用静态的性能损失(如果有的话)是否值得。
  • 请注意,上面的思考可能对提问者无用。访问的范围很好,但 RNG 变量的生命周期可能很长。傻瓜们将一直参与该计划。

标签: c++ optimization


【解决方案1】:

您可以使用带有初始化程序的捕获(但 lambda 必须是 mutable,因为生成器不能是 const):

std::vector<int> dice_samples(10);

std::generate(dice_samples.begin(), dice_samples.end(), [m_gen=std::mt19937_64{}, dist=std::uniform_int_distribution<int>{}]() mutable {
    return dist(m_gen);
});

或者您可以创建另一个范围:

std::vector<int> dice_samples(10);

{
    std::mt19937_64 m_gen;
    std::uniform_int_distribution<int> dist(1, 6);

    std::generate(dice_sample.begin(), dice_sample.end(), [&](){
        return dist(m_gen);
    });
}

【讨论】:

    【解决方案2】:

    有多种可能的解决方案。首先,您可以将变量和对std::generate 的调用放在一个范围内:

    std::vector<int> dice_samples(10);
    {
        std::mt19937_64 m_gen;
        std::uniform_int_distribution<int> dist(1, 6);
        std::generate(dice_sample.begin(), dice_sample.end(), [&m_gen, &dist](){
            return dist(m_gen);
        });
    }
    

    您还可以在 lambda 中使用 static 变量:

    std::vector<int> dice_samples(10);
    std::generate(dice_sample.begin(), dice_sample.end(), [&m_gen, &dist](){
        static std::mt19937_64 m_gen;
        static std::uniform_int_distribution<int> dist(1, 6);
        return dist(m_gen);
    });
    

    请注意,static 可变变量会带来一些开销,因为编译器需要在第一次以线程安全的方式调用函数时初始化它们:

    在块范围内使用说明符 static 或 thread_local (C++11 起) 声明的变量具有静态或线程 (C++11 起) 存储持续时间,但在控制第一次通过它们的声明时被初始化(除非它们的初始化是零或常量初始化,可以在第一次进入块之前执行)。在所有进一步的调用中,都会跳过声明。

    (见static local variables

    在您的情况下,这应该可以忽略不计。 还要记住,改变 static 变量不是线程安全的。您在使用 PRNG 时对其进行了变异。

    【讨论】:

    • 由于 lambda 是编译器生成的类型,并且在声明 lambda 时会实例化该类型,因此不会使变量在 lambda 内成为 static 导致它们的生命周期为调用 lambda 后的程序?如果是这样,最好使用本地范围,以便在每次需要新的 generate() 调用时创建和销毁新变量。
    • @RemyLebeau 在程序启动时初始化所有static 局部变量会更有效,但它会违反标准。见编辑。在实践中,也许编译器会检测到 PRNG 可以在编译时进行初始化。不过,这取决于它的实现。
    猜你喜欢
    • 2016-02-15
    • 1970-01-01
    • 2019-03-26
    • 2017-04-06
    • 1970-01-01
    • 2015-12-26
    • 2014-01-31
    • 1970-01-01
    相关资源
    最近更新 更多