1つの(デフォルト以外の)パラメーターを持つコンストラクターは、そのパラメーター型からクラス型に変換する暗黙のコンバーターのように機能することを理解しています。ただし、explicit
を使用して、パラメーターのないコンストラクター(デフォルトのコンストラクター)または2つ以上のパラメーターを持つコンストラクター(デフォルト以外)を修飾できます。
これらのコンストラクターで明示的に許可されるのはなぜですか?これが何らかの暗黙の変換を防ぐのに役立つ例はありますか?
確かに一つの理由はそれが傷つかないからです。
それが必要な理由の1つは、最初のパラメーターにデフォルトの引数がある場合です。コンストラクターはデフォルトのコンストラクターになりますが、変換コンストラクターとして使用できます。
struct A {
explicit A(int = 0); // added it to a default constructor
};
C++ 0xは、マルチパラメーターコンストラクターに実際に使用します。 C++ 0xでは、初期化子リストを使用してクラスオブジェクトを初期化できます。哲学は
= { ... }
を使用する場合は、オブジェクトの抽象値を概念的に表し、型に変換したい一種の「複合値」でオブジェクトを初期化します。
{ ... }
初期化子を使用する場合は、オブジェクトのコンストラクターを直接呼び出します。必ずしも変換を指定する必要はありません。
この例を考えてみましょう
struct String {
// this is a non-converting constructor
explicit String(int initialLength, int capacity);
};
struct Address {
// converting constructor
Address(string name, string street, string city);
};
String s = { 10, 15 }; // error!
String s1{10, 15}; // fine
Address a = { "litb", "nerdsway", "frankfurt" }; // fine
このように、C++ 0xは、他のコンストラクターで明示的に許可するというC++ 03の決定は、まったく悪い考えではなかったことを示しています。
おそらくそれはメンテナンスをサポートすることでした。複数引数のコンストラクターでexplicit
を使用することにより、引数にデフォルトを追加するときに誤って暗黙の変換を導入することを回避できます。私はそれを信じていませんが;代わりに、C++では、言語定義を以前よりも複雑にしないために、多くのことが許可されているだけだと思います。
おそらく最も悪名高いケースは、非static
ローカル変数への参照を返すことです。他に影響を与えることなくすべての「無意味な」ものを除外するには、追加の複雑なルールが必要になります。したがって、許可されているだけで、その参照をseするとUBが生成されます。
または、コンストラクターの場合、署名が異なる限り、デフォルトのコンストラクターをいくつでも定義できますが、コンストラクターが複数ある場合は、デフォルトでそれらのいずれかを呼び出すのはかなり困難です。 :-)
より良い質問は、おそらく、変換演算子でexplicit
も許可されないのはなぜですか?
そうですね、C++ 0xではそうなるでしょう。ですから、そうしない理由はありませんでした。変換演算子でexplicit
を許可しない実際の理由は、見落としと同じくらい乱暴であるか、そもそもexplicit
を採用するのに苦労している、または委員会の時間の単純な優先順位付けなどです。
乾杯&hth。、
High Integrity C++ Coding Standardによると、すべてのsinlgeパラメーターコンストラクターをexplicit型変換での偶発的な使用を回避するため。複数引数のコンストラクターの場合、複数のパラメーターを受け入れるコンストラクターがあり、それぞれにデフォルト値があり、コンストラクターをある種のデフォルトコンストラクターと変換コンストラクターに変換するとします。
class C {
public:
C( const C& ); // ok copy
constructor C(); // ok default constructor
C( int, int ); // ok more than one non-default argument
explicit C( int ); // prefer
C( double ); // avoid
C( float f, int i=0 ); // avoid, implicit conversion constructor
C( int i=0, float f=0.0 ); // avoid, default constructor, but
// also a conversion constructor
};
void bar( C const & );
void foo()
{
bar( 10 ); // compile error must be 'bar( C( 10 ) )'
bar( 0.0 ); // implicit conversion to C
}