[解決済み] なぜラムダのサイズは1バイトなのですか?
質問
私はC++でいくつかのラムダのメモリを扱っていますが、そのサイズに少し困惑しています。
以下は私のテストコードです。
#include <iostream>
#include <string>
int main()
{
auto f = [](){ return 17; };
std::cout << f() << std::endl;
std::cout << &f << std::endl;
std::cout << sizeof(f) << std::endl;
}
ouptutは。
17
0x7d90ba8f626f
1
これは、私のラムダのサイズが1であることを示唆しています。
-
これはどのようにして可能なのでしょうか?
-
ラムダは最低限、その実装へのポインタであるべきではないでしょうか?
どのように解決するのですか?
問題のラムダは、実際には 状態なし .
調べる。
struct lambda {
auto operator()() const { return 17; }
};
そして、もし
lambda f;
となっていれば、それは空のクラスです。 だけでなく、上記の
lambda
は機能的にラムダと似ているだけでなく、ラムダがどのように実装されているかも示しています。 (また、関数ポインタ演算子への暗黙のキャストが必要であり、名前も
lambda
はコンパイラが生成した疑似GUIDに置き換えられます)
C++では、オブジェクトはポインタではありません。 それらは実際のものです。 オブジェクトは、その中にデータを格納するために必要なスペースだけを使用します。 オブジェクトへのポインタはオブジェクトより大きくなることがあります。
ラムダを関数へのポインタと考えるかもしれませんが、そうではありません。 を再代入することはできません。
auto f = [](){ return 17; };
を別の関数やラムダに再指定することはできません!
auto f = [](){ return 17; };
f = [](){ return -42; };
上記は
違法
. に空きがありません。
f
を格納するための
どの
関数が呼び出されるのか -- その情報は
型に格納されます。
の
f
の値ではなく
f
!
こうすれば
int(*f)() = [](){ return 17; };
またはこれ
std::function<int()> f = [](){ return 17; };
の場合、もはやラムダを直接保存しているわけではありません。 どちらの場合も
f = [](){ return -42; }
は合法なので、これらのケースでは
どの
関数を呼び出すかを
f
. そして
sizeof(f)
はもはや
1
ではなく、むしろ
sizeof(int(*)())
またはそれ以上のサイズにする(基本的にポインタサイズかそれ以上のサイズにする、期待通り。
std::function
は標準によって暗示された最小サイズ(それらはあるサイズまで "自身の内部" callables を格納できる必要があります)を持ち、それは実際には少なくとも関数ポインタと同じ大きさです)。
では
int(*f)()
の場合、そのラムダを呼び出したかのように振る舞う関数への関数ポインタを保存していることになります。 これはステートレスなラムダ (空の
[]
キャプチャーリストが空のもの)にのみ有効です。
では
std::function<int()> f
の場合、型消去クラスを作成することになります。
std::function<int()>
のインスタンスを作成し、(この場合) placement new を使用して size-1 ラムダのコピーを内部バッファに格納します (より大きなラムダが渡された場合 (より多くの状態を含む) は、ヒープ割り当てを使用します)。
推測ですが、これらのようなことが、おそらく皆さんが考えていることだと思います。 ラムダはシグネチャによって型が記述されるオブジェクトであること。 C++では、ラムダを
ゼロコスト
を抽象化することにしました。 これによって、ラムダを
std
アルゴリズム(または類似のもの)にラムダを渡し、コンパイラがアルゴリズムテンプレートをインスタンス化するときに、その内容が完全に見えるようにすることができます。 もしラムダが以下のような型を持っていたら
std::function<void(int)>
のような型を持つ場合、そのコンテンツは完全に表示されず、手作りの関数オブジェクトの方が速いかもしれません。
C++の標準化の目標は、手作りのCコードに対するオーバーヘッドをゼロにした高レベルのプログラミングです。
今、あなたは自分の
f
が実際にステートレスであることを理解したところで、あなたの頭の中にはもう一つの疑問があるはずです:ラムダはステートを持ちません。 なぜサイズに
0
?
簡単な答えがあります。
C++のすべてのオブジェクトは、標準では最小のサイズ1でなければならず、同じ型の2つのオブジェクトは同じアドレスを持つことができません。 これらはつながっていて、なぜなら型の配列は
T
型の配列は、その要素が
sizeof(T)
に配置されます。
さて、それは状態を持たないので、時にはスペースを取らないことがあります。 これは、それが単独でいるときには起こりえませんが、いくつかの文脈では起こりえます。
std::tuple
といったライブラリのコードは、この事実を利用しています。 以下はその仕組みです。
ラムダはクラスと同じように
operator()
オーバーロードされたステートレスなラムダ(
[]
捕捉リストを持つ) はすべて空のクラスです。 これらは
sizeof
の
1
. 実際、それらを継承した場合 (これは許可されています!) 、それらはスペースを取らないでしょう。
同じタイプのアドレスの衝突を引き起こさない限りは
. (これは空ベース最適化として知られています)。
template<class T>
struct toy:T {
toy(toy const&)=default;
toy(toy &&)=default;
toy(T const&t):T(t) {}
toy(T &&t):T(std::move(t)) {}
int state = 0;
};
template<class Lambda>
toy<Lambda> make_toy( Lambda const& l ) { return {l}; }
その
sizeof(make_toy( []{std::cout << "hello world!\n"; } ))
は
sizeof(int)
(ただし、評価されていないコンテキストでラムダを作成することはできないので、上記は違法です。
auto toy = make_toy(blah);
を作り、次に
sizeof(blah)
を実行する。ただし、これは単なるノイズである)。
sizeof([]{std::cout << "hello world!\n"; })
はやはり
1
(類似の資格)です。
別のおもちゃの種類を作ると
template<class T>
struct toy2:T {
toy2(toy2 const&)=default;
toy2(T const&t):T(t), t2(t) {}
T t2;
};
template<class Lambda>
toy2<Lambda> make_toy2( Lambda const& l ) { return {l}; }
これには
2つのコピー
というラムダがあります。 同じアドレスを共有することができないため
sizeof(toy2(some_lambda))
は
2
!
関連
-
[解決済み] string does not name a type Errorが発生するのはなぜですか?
-
[解決済み] クラスにデフォルトコンストラクタが存在しない。
-
[解決済み】fpermissiveフラグは何をするのですか?
-
[解決済み] explicit キーワードの意味は?
-
[解決済み] using namespace std;」はなぜバッドプラクティスだと言われるのですか?
-
[解決済み] 要素ごとの加算は、結合ループよりも分離ループの方がはるかに高速なのはなぜですか?
-
[解決済み] C++11では、標準化されたメモリモデルが導入されました。その意味するところは?そして、C++プログラミングにどのような影響を与えるのでしょうか?
-
[解決済み] なぜ、オブジェクトそのものではなく、ポインタを使用しなければならないのですか?
-
[解決済み] 0.1fを0にすると、なぜ10倍もパフォーマンスが落ちるのですか?
-
[解決済み】C++11のラムダ式って何?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Visual Studio 2015で「非標準の構文。'&'を使用してメンバーへのポインターを作成します」エラー
-
[解決済み】C++コンパイルタイムエラー:数値定数の前に期待される識別子
-
[解決済み】C++ 式はポインタからオブジェクトへの型を持っている必要があります。
-
[解決済み】Cygwin Make bash コマンドが見つかりません。
-
[解決済み】関数名の前に期待されるイニシャライザー
-
[解決済み】エラー:strcpyがこのスコープで宣言されていない
-
[解決済み】C++の余分な資格エラー
-
[解決済み】クラステンプレートの使用にはテンプレート引数リストが必要です
-
[解決済み】std::cin.getline( ) vs. std::cin
-
[解決済み] 配列のベクトルを扱う正しい方法