C++ の明示的なコンストラクターとイテレーター
-
18-09-2019 - |
質問
次のコードを考えてみましょう。
#include <vector>
struct A
{
explicit A(int i_) : i(i_) {}
int i;
};
int main()
{
std::vector<int> ints;
std::vector<A> As(ints.begin(), ints.end());
}
上記はコンパイルできるでしょうか?私の感覚では、コンストラクターがマークされているため、そうすべきではないと思います explicit
.
Microsoft Visual C++ もこれに同意し、明確なエラー メッセージを表示します。 cannot convert from 'int' to 'const A'; Constructor for struct 'A' is declared 'explicit'
ただし、使用すると、 Comeau のオンライン コンパイラ, 、コードは正常にコンパイルされます。
どちらが正しい?
編集:
興味深いのは、変化すること vector
に set
(追加後 operator <
A) を実行すると、両方のコンパイラでエラーが発生します。
ただし、変更する vector<int>
に map<int, int>
そして vector<A>
に map<A, A>
両方のコンパイラがコードを受け入れるようになります。
解決
GCC の STL 実装を調べてみましたが、同様の動作をするはずです。その理由は次のとおりです。
- の要素
vector
任意の 2 つの型を受け入れる汎用関数テンプレートによって初期化されます。X
そしてV
そして電話しますnew( p ) X( v )
どこv
ですV
(少し意訳しています)。これにより、明示的な変換が可能になります。 - の要素
set
またはmap
のプライベートメンバー関数によって初期化されます。_tree<T,…>
具体的にはT const &
渡される。このメンバー関数は (テンプレートのメンバーである以上に) テンプレートではないため、初期値を暗黙的に次のように変換できない場合は、T
, 、通話は失敗します。(ここでもコードを簡略化しています。)
標準では、範囲を使用してコンテナーを初期化するときに、明示的な変換作業や暗黙的な変換が機能しないことは要求されていません。これは単に範囲がコンテナにコピーされたことを示しているだけです。あなたの目的にとっては間違いなく曖昧です。
次のような問題を考慮して標準をどのように改良したかを考えると、このような曖昧さが存在することは驚くべきことです。 私が持っていたもの 数週間前。
他のヒント
私はそれがstd::vector<A> As(Iterator,Iterator)
はSTLのあなたの特定の実装に実装されているかに依存すると思います。
これはかなりトリッキーな問題であり、それは(これは信じていることは本当に難しいようです)VisualStudioのは間違って右とコモである場合があります。
ワード単位で読み出す場合規格は、(引用を参照)。のコピーコンストラクタの観点から、そのベクトルのコンストラクタを定義し、それは文字通り反復子を間接参照して得られたオブジェクトが最初に変換されなければならないことを意味しますタイプT、その後、コピーコンストラクタを呼び出す必要があります。この時点で、明示的なコンストラクタでのコードはコンパイルべきではありません。
直接コンストラクタ呼び出しは、明示的になりますので、コードをコンパイルする必要があり、その場合には、引数として間接参照イテレータを取るコンストラクタを呼び出すために、一方で、実施を予想するのが妥当と思われます。これは、のコピーコンストラクタとしては型Tのオブジェクトへの単一の可能性が一定の基準を取るコンストラクタとして指定されたタイプTのために定義されます。
私は、ベクトルコンストラクタの複雑さに関しては、標準で文言がおそらく必要と言い換えるべきであるということである(これは単なる個人的な意見です)コモのアプローチを使用しない任意の合理的な引数を考えることができない、と信じている私の< EM>のみNは、適切な場合には、コールT( *first )
(つまり、いずれかの値またはおそらくは一定の基準によってInputIterator::value_type
を取るコンストラクタ()と一致するコンストラクタのように定義されなければならない、適切なTコンストラクタの呼び出し、又はTはInputIterator::value_type
からTへの暗黙の変換後のコンストラクタをコピーします。
23.2.4.1 [lib.vector.cons] / 1
複雑さ:コンストラクタのテンプレートベクトル(InputIterator 最初、最後InputIteratorは)のみになり NはTのコピーコンストラクタの呼び出し (ここで、Nは、の間の距離であります 最初と最後)と無再配分 イテレータは、最初と最後である場合に 前方、双方向、またはランダム アクセスカテゴリ。これは、順番Nを作ります Tのコピーコンストラクタを呼び出し、 オーダーログN再配分彼らがしている場合 ただ入力イテレータます。
私が与えられたとき、VSコンパイラがどのように動作するかを知っていただきたいと思います:
struct T1;
struct T2 {
operator T1 ();
};
struct T1 {
T1( T2 const & ) { std::cout << "T1(T2)" << std::endl; }
};
T2::operator T1() {
std::cout << "T2::operator T1" << std::endl;
return T1(*this);
}
int main() {
std::vector<T2> v2;
v2.push_back( T2() );
std::vector<T1> v1( v2.begin(), v2.end() );
}
gの結果++はT2::operator T1
が呼び出されていない、むしろv1
の要素をv2
の要素から直接構築されることです。私はVSで、コンパイラがT1要素にT2::operator T1
の各要素から変換し、コピーコンストラクタを呼び出すためにv2
を使用することを前提としています。そうですか。
これは本当にSTLライブラリがどのように実装されるかの問題ではなく、言語仕様の問題に帰着します。何も働いてからこれを禁止する言語仕様ではありません、またそれが動作する必要があることが必要となるものがあります。
STL ::ベクトルコンストラクタは代入演算子を使用して暗黙的な変換を試みるために書かれていた場合は、、それが失敗します。これは、マイクロソフトのSTLの実装は、このコードが正常に動作します。その場合には、コンストラクタの呼び出しを経由して初期化中に、戻り値の最適化を使用する可能性が高いです。
STL ::ベクトルコンストラクタはテンプレートであるため、この作品だけの理由があることに注意することが重要であり、そして唯一の要件は、それが入力イテレータのすべての必要な機能をサポートしていることをより正確にinput_iteratorである、またはということですます。
私はまた、これは、クロスプラットフォームのコードを記述することはしばしば困難である理由の最たるものであることを指摘したいと思います。時には、あなたはどちらのコンパイラは、必ずしも言語標準から外れたが、コードはまだ移植ではない問題になってしまう。
このコードは、コモでコンパイルされません。
class Foo
{
public:
explicit Foo(int bar)
{
}
};
class Bar
{
void DoStuff(Foo foo){
}
void DoStuff2()
{
DoStuff(4);
}
};
エラーメッセージ:
"ComeauTest.c", line 16: error: no suitable constructor exists to convert from "int"
to "Foo"
DoStuff(4);
^
1 error detected in the compilation of "ComeauTest.c".
は、だから、初歩的なレベルでのオンラインコンパイラは、明示的なコンストラクタをサポートしています。ベクトル/イテレータとは何かをしなければなりません。
編集しかし、これはコンパイルされます:
Foo foo = (Foo)5;
は明示的な変換であるので、それはOKです。私の推測では、コモベクトルクラスは、コンストラクタで明示的なキャストはどこか、どこMicrosoftのライブラリがないですん。
明示的なコンストラクタの詳細を - http://www.glenmccl.com/tip_023.htm
はい、それはコンパイルする必要があります。コンストラクタが使用されていない場合、その明示は問題ではありません。