web-dev-qa-db-ja.com

C ++ 11で許可されていないラムダの再定義、なぜですか?

例:

#include <functional>

int main() {
  auto test = []{};
  test = []{};

  return 0;
}

これにより、gcc4.7.2で次のエラーメッセージが出力されます。

test.cpp: In function ‘int main()’:
test.cpp:5:13: error: no match for ‘operator=’ in ‘test = <lambda closure object>main()::<lambda()>{}’
test.cpp:5:13: note: candidate is:
test.cpp:4:16: note: main()::<lambda()>& main()::<lambda()>::operator=(const main()::<lambda()>&) <deleted>
test.cpp:4:16: note:   no known conversion for argument 1 from ‘main()::<lambda()>’ to ‘const main()::<lambda()>&’

標準5.1.2.3(強調鉱山)から:

実装は、以下を変更する以外にプログラムの観察可能な動作を変更しない限り、以下に説明するものとは異なる方法でクロージャタイプを定義できます。

—クロージャータイプのサイズおよび/または配置、

—クロージャタイプが簡単にコピー可能(第9節)かどうか

—クロージャタイプが標準レイアウトクラス(第9節)であるか、それとも

—クロージャタイプがPODクラスであるかどうか(第9節)。

私が知る限り、これは私が直面していることです。削除された代入演算子を使用しようとして失敗しました。簡単な回避策があるかどうか、そしてもっと広くラムダのコピー構成可能性を省略できるようにする動機付けの根拠が何であるかを知りたいと思います。

61

これらの2つのラムダは同じ型であると思われるようですが、そうではありません。それぞれが独自のタイプを作成します。

#include <functional>
#include <type_traits>
#include <iostream>

int main() {
  auto test = []{};
  auto test2 = []{};
  std::cout << std::is_same< decltype( test ), decltype( test2 ) >::value << std::endl;
  return 0;
}

0を出力します。もちろん、コンパイラから受け取るエラーメッセージは、この点で少し明確になる可能性があります...

69
Daniel Frey

ラムダ式の型(クロージャオブジェクトの型でもあります)はnique、名前のない非ユニオンクラス型です

つまり、次のことをしているようなものです。

struct {} a;
struct {} b;
a = b; // error, type mismatch

使用する std::function同じシグネチャを持つ異なるラムダを同じ変数に割り当てたい場合。

std::function<void()> f = []{};
f = []{}; //ok
47
Andrew Tomazos

各ラムダは異なる匿名の互換性のないタイプであるため、ラムダを再定義することはできません。それらをコピーできるのは、その型を推測できるテンプレート関数(std::function ctorなど)に渡す場合のみです。

8
J.N.

これを実行できない理由は、ラムダ式のコピー割り当て演算子が削除済みと宣言されているためです。標準のセクション5.1.2/20を参照してください。より明確なもの(明確な定義の異常な定義)については、このコードサンプルを参照してください

template<class T> void f(T x1)
{
  T x2 = x1; // copy constructor exists, this operation will succeed.
  x2 = x1; // assignment operator, deleted and will cause an error
}
int main()
{
  f([]{});
  return 0;
}

他の回答では、各ラムダには一意のタイプがあると指摘されていますが、これがエラーが発生する理由ではありません。この例は、2つのラムダが同じタイプであっても、それをコピーできないことを示しています。ただし、それを新しい変数にコピーすることはできます。これが、エラーメッセージがoperator=の欠落について不平を言っている理由であり、タイプが異なることについてではありません。独自のタイプを持つ各ラムダもあまり役に立ちませんが。

5
Rastaban

あるラムダを別のタイプの別のラムダに割り当てることができる場合、そのラムダから別のラムダに関数本体/定義をコピーするにはどうすればよいですか?頑固な場合は、メンバーを使用できますstd::function-コピーされるタイプのようなタイプ。しかし、それは何とか何とか支払わないという古いC++ルールに反するでしょう...

2
Mark Garcia