web-dev-qa-db-ja.com

ラムダの呼び出し演算子が暗黙的にconstなのはなぜですか?

以下の関数に小さな「ラムダ式」があります。

int main()
{
    int x = 10;
    auto lambda = [=] () { return x + 3; };
}

以下は、上記のラムダ式に対して生成された「匿名クロージャクラス」です。

int main()
{
    int x = 10;

    class __lambda_3_19
    {
        public: inline /*constexpr */ int operator()() const
        {
            return x + 3;
        }

        private:
            int x;

        public: __lambda_3_19(int _x) : x{_x}
          {}

    };

    __lambda_3_19 lambda = __lambda_3_19{x};
}

コンパイラによって生成されたクロージャの「operator()」は暗黙的にconstです。標準委員会がデフォルトでconstにしたのはなぜですか?

19
cpp_enthusiast

cppreference から

キーワードmutableがラムダ式で使用されない限り、関数呼び出し演算子はconst修飾され、コピーによってキャプチャされたオブジェクトはこのoperator()内から変更できません。

あなたの場合、コピーでキャプチャされた変更可能なものはありません。

何かを書くと

_int x = 10;

auto lambda = [=] () mutable { x += 3; return x; };
_

constが消えるはずです

-編集-

正確なOP

Mutableを追加することで問題が解決することはすでにわかっていました。問題は、デフォルトでラムダを不変にする理由を理解したいということです。

私は言語弁護士ではありませんが、これは明白なようです:constではなくoperator()を作成すると、

_template <typename F>
void foo (F const & f)
 { f(); }

// ...

foo([]{ std::cout << "lambda!" << std::endl; });
_

つまり、operator()constでない場合、const参照として渡すラムダを使用できません。

また、厳密に必要でない場合は、許容できない制限にする必要があります。

9
max66

これを見つけてください 論文 open-std.orgのHerb Sutterがこの問題について議論しています。

奇妙なカップル:値の注入されたconstおよび風変わりな可変によるキャプチャ
プログラマが値でローカル変数をキャプチャし、キャプチャされた値(ラムダオブジェクトのメンバ変数)を変更しようとするこのストローマンの例を検討してください。

int val = 0;
auto x = [=]( item e ) // look ma, [=] means explicit copy
 { use( e, ++val ); }; // error: count is const, need ‘mutable’
auto y = [val]( item e ) // darnit, I really can’t get more explicit
 { use( e, ++val ); }; // same error: count is const, need ‘mutable’

この機能は、ユーザーがコピーを取得したことに気付かない可能性があり、特にラムダがコピー可能であるため、別のラムダのコピーを変更している可能性があるという懸念から追加されたようです

上記の引用と例は、標準化委員会mightがデフォルトでconstにし、それを変更するためにmutableを必要とした理由を示しています。

18
P.W

ラムダ内の変数が最初にキャプチャされたものを参照していない場合、単に混乱を避けるためだと思います。字句的には、そのような変数は、その「元の」範囲内にあるかのようです。コピーは主にオブジェクトの寿命を延ばすためです。キャプチャがコピーによるものではない場合、オリジナルを参照し、オリジナルに変更が適用され、2つの異なるオブジェクト(1つは暗黙的に導入される)による混乱はなく、ラムダのconst関数呼び出し演算子で許可されます。

0
guest