私が知る限り、この問題は ではなくのコンストラクターが原因ですmap::emplace
。pair
#include <map>
struct A
{
A(int) {}
A(A&&) = delete;
A(A const&) = delete;
};
int main()
{
std::pair<int, A> x(1, 4); // error
}
私が知る限り、このコード例は、coliru の g++4.8.1 でも clang++3.5 でもコンパイルされません。どちらも libstdc++ を使用しています。
この問題は、構築することはできますが、
A t(4);
つまり、 を暗黙的に [conv]/3 に変換することはstd::is_constructible<A, int>::value == true
できませんint
A
ある発明された一時変数に対して、宣言が適切な形式である場合にのみ、式を暗黙的に型に変換できe
ます。T
T t=e;
t
コピーの初期化 ( =
) に注意してください。これにより、テンポラリが作成され、このテンポラリ [dcl.init]/17 からA
初期化されます。t
一時的な からのこの初期化は、 の削除された move ctor を呼び出そうとA
するため、変換が正しくありません。
int
an からan に変換できないため、呼び出されると予想されるそのA
コンストラクターはSFINAE によって拒否されます。この動作は驚くべきものです。N4387 - ペアとタプルの改善は、コンストラクターを拒否するのではなく作成することで、状況を改善しようとします。N4387 は Lenexa 会議で C++1z に投票されました。pair
explicit
次に、C++11 の規則について説明します。
呼び出されると思っていたコンストラクタは [pairs.pair]/7-9 に記述されています
template<class U, class V> constexpr pair(U&& x, V&& y);
7 必須: is_constructible<first_type, U&&>::value
istrue
および
is_constructible<second_type, V&&>::value
is true
.
8 効果:コンストラクターは最初に で初期化し、次に で初期化std::forward<U>(x)
し
std::forward<V>(y)
ます。
9 備考:U
暗黙的に変換できない
場合、first_type
またはこのコンストラクターV
に暗黙的に変換できない場合、オーバーロードの解決に参加してはなりません。second_type
Requiresis_constructible
セクションの違いと、Remarksセクションの "is not implicitly convertible"の違いに注意してください。このコンストラクターを呼び出すための要件は満たされていますが、オーバーロードの解決に参加しない場合があります (= SFINAE を介して拒否する必要があります)。
したがって、オーバーロードの解決では、「より悪い一致」、つまり 2 番目のパラメーターが a のものを選択する必要がありますA const&
。引数からテンポラリが作成され、int
この参照にバインドされます。参照は、pair
データ メンバ ( .second
) の初期化に使用されます。初期化で、 の削除されたコピー ctor を呼び出そうとしましたA
が、ペアの構成が正しくありません。
libstdc++ には (拡張機能として) いくつかの非標準の ctor があります。最新の doxygen (および 4.8.2) で、pair
呼び出されると予想していたコンストラクターは次のとおりです (標準で要求される規則に驚いています)。
template<class _U1, class _U2,
class = typename enable_if<__and_<is_convertible<_U1, _T1>,
is_convertible<_U2, _T2>
>::value
>::type>
constexpr pair(_U1&& __x, _U2&& __y)
: first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y)) { }
実際に呼び出されるのは非標準です。
// DR 811.
template<class _U1,
class = typename enable_if<is_convertible<_U1, _T1>::value>::type>
constexpr pair(_U1&& __x, const _T2& __y)
: first(std::forward<_U1>(__x)), second(__y) { }
このプログラムは、標準によれば形式が正しくありません。この非標準の ctor によって単に拒否されたわけではありません。
最後に、is_constructible
との仕様を次に示しis_convertible
ます。
is_constructible
[メタ.rel]/4
次の関数プロトタイプがあるとします。
template <class T>
typename add_rvalue_reference<T>::type create();
テンプレートの特殊化の述語条件はis_constructible<T, Args...>
、次の変数定義が何らかの発明された変数に対して整形式である場合にのみ満たされますt
。
T t(create<Args>()...);
[注:これらのトークンは、関数宣言として解釈されることはありません。—終わりの注T
] アクセス チェックは、 および のいずれとも無関係なコンテキストであるかのように実行されますArgs
。変数の初期化の直接のコンテキストの有効性のみが考慮されます。
is_convertible
[meta.unary.prop]/6:
次の関数プロトタイプがあるとします。
template <class T>
typename add_rvalue_reference<T>::type create();
テンプレートの特殊化の述語条件is_convertible<From, To>
は、関数の戻り値の型への暗黙の変換を含め、次のコードの戻り値式が整形式である場合にのみ満たされます。
To test() {
return create<From>();
}
[注:この要件により、参照型、void 型、配列型、および関数型について明確に定義された結果が得られます。—終わりの注To
] アクセス チェックは、および とは無関係のコンテキストであるかのように実行されますFrom
。return-statement (戻り型への変換を含む)の式の直接のコンテキストの有効性のみが考慮されます。
あなたのタイプについてA
は、
A t(create<int>());
整形式です。でも
A test() {
return create<int>();
}
タイプのテンポラリを作成し、それを戻り値に移動A
しようとします(コピー初期化)。これは削除されたctorを選択するため、形式が正しくありません。A(A&&)