11

次のコードは、GCC 4.7.2 または Clang 3.2 ではコンパイルできません。

#include <vector>
#include <functional>

int main()
{
   std::vector<std::function<void()>> a;
   std::vector<std::function<void()>> b{a};
}

問題は、明らかにコピー コンストラクターを呼び出す必要があるときに、コンパイラーが initializer_list を使用して b を作成しようとすることです。ただし、標準では initializer_list コンストラクターを優先する必要があるため、これは望ましい動作のようです。

このコードは、他の std::vector に対しては正常に機能しますが、std::function の場合、コンパイラは、initializer_list コンストラクターが必要か、別のコンストラクターが必要かを認識できません。

それを回避する方法はないようです。その場合、テンプレート化されたコードで均一な初期化を使用することはできません。これは非常に残念なことです。

一方、Visual Studio (2012 年 11 月の CTP) はこれについて文句を言いません。しかし、initializer_list のサポートは現時点ではあまり良くないため、バグである可能性があります。

4

2 に答える 2

11

これはLWG 2132であり、まだ欠陥レポートではありませんが、修正するための明確なコンセンサス (および実装経験) があります。標準では、std::functionのコンストラクターは任意の型を受け入れると規定されているため、実行可能な場合は初期化子リスト コンストラクターが他のコンストラクターよりも常に優先されるため、コードstd::initializer_list<std::function<void()>>は object から初期化された単一の要素を使用してからベクトルを構築しようとしますastd::function<void()>結果のオブジェクトからを構築することはできaますが、呼び出し可能ではないため、エラーが発生します。

言い換えれば、問題は、std::function任意の型からの変換を可能にする制約のないテンプレート コンストラクターがあることです。初期化子リストコンストラクターは実行可能な場合は他のコンストラクターよりも優先され、制約のないfunctionコンストラクターは常に任意の型から作成できるinitializer_list<function<void()>>ため、初期化子リストコンストラクターは常に実行可能であるため、これは問題を引き起こします。

提案された 2132 への解決策ではstd::function、呼び出し不可能な型から a を構築することができないため、initializer-list コンストラクターは実行できず、vector代わりにコピー コンストラクターが呼び出されます。 その解決策を GCC 4.8に実装しましたが、Clang の libc++ ライブラリにも既に実装されています。

于 2012-12-30T12:29:38.170 に答える
5

これがコンパイルされるべきではなく、gcc(バージョン4.8.0 20121111)とclang(バージョン3.3(トランク171007))の両方がコードをコンパイルする理由がわかりません。とはいえ、「均一な初期化」は均一とはほど遠いものです。コンストラクターを呼び出すときに中括弧を使用できない場合が確実にあります。

于 2012-12-30T01:58:27.963 に答える