LoginSignup
15
14

More than 5 years have passed since last update.

C++のラムダ式の参照キャプチャは危険がいっぱい?

Posted at

JavaScript でこんな感じにクロージャーを使うと、

function get_func()
{
    var val=0;
    return function(){
        return ++val;
    };
}

カウンタのように動作しますが、

var a = get_func();
var b = get_func();

console.log(a()); // 1
console.log(a()); // 2
console.log(b()); // 1
console.log(b()); // 2

C++ のラムダ式で次のようにすると動作しません。

#include <iostream>
#include <functional>

std::function<int()> get_func()
{
    int val = 0;
    return [&]() -> int {
        return ++val;
    };
}

int main()
{
    auto a = get_func();
    auto b = get_func();

    std::cout << a() << std::endl;
    std::cout << a() << std::endl;
    std::cout << b() << std::endl;
    std::cout << b() << std::endl;

    return 0;
}

クロージャーじゃないので get_func() 関数を抜けた時点で val は無くなっており、ラムダ式が参照キャプチャしている val は不正な位置を指しているからです。

次のようなコードがダメなのと同じです。

#include <iostream>

int* get_func()
{
    int val;
    return &val;
}

int main()
{
    auto a = get_func();
    auto b = get_func();

    std::cout << ++*a << std::endl;
    std::cout << ++*a << std::endl;
    std::cout << ++*b << std::endl;
    std::cout << ++*b << std::endl;

    return 0;
}

C++ な人にはごくごく当たり前のことだし、同じに考えるほうがオカシイのだろうけど、他の言語になれた人は引っかかりそうな気がします。


最初の JavaScript のようなことを C++ でやりたければどうすべきなのでしょうかね・・・

こんな感じ?

#include <iostream>
#include <functional>

std::function<int()> get_func()
{
    class func
    {
    public:
        func() : val(0) {}
        int operator()()
        {
            return ++val;
        }
    private:
        int val;
    };

    return func();
}

int main()
{
    auto a = get_func();
    auto b = get_func();

    std::cout << a() << std::endl;
    std::cout << a() << std::endl;
    std::cout << b() << std::endl;
    std::cout << b() << std::endl;

    return 0;
}
15
14
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
14