複数の引数を取る明示的なコンストラクタ


88

複数の引数をexplicit持つコンストラクターを作成すると、(有用な)効果がありますか?

例:

class A {
    public:
        explicit A( int b, int c ); // does explicit have any (useful) effect?
};

回答:


120

C ++ 11までは、ええ、explicit複数引数のコンストラクターで使用する理由はありません。

初期化リストがあるため、C ++ 11ではこれが変更されます。基本的に、初期化子リストを使用したコピー初期化(直接初期化ではない)では、コンストラクターにマークを付けない必要がありますexplicit

例:

struct Foo { Foo(int, int); };
struct Bar { explicit Bar(int, int); };

Foo f1(1, 1); // ok
Foo f2 {1, 1}; // ok
Foo f3 = {1, 1}; // ok

Bar b1(1, 1); // ok
Bar b2 {1, 1}; // ok
Bar b3 = {1, 1}; // NOT OKAY

5
この答えは、「なぜそれが欲しいのか」または「これはいつ役立つのか」という説明の方が良いと思います。
MateuszL

@MateuszL Edgarの答えは、なぜそれが役立つのか(そして間違いなくチェックに値する)についておそらく最良の議論を与えます。ただし、そこにある理由は、それがの既存のセマンティクスの論理的な拡張であるためexplicitです。私は個人的にマルチ引数コンストラクターを作成することを気にしませんexplicit
sneftel

31

中括弧の初期化のために(たとえば配列で)それに遭遇するでしょう

struct A {
        explicit A( int b, int c ) {}
};

struct B {
         B( int b, int c ) {}
};

int main() {
    B b[] = {{1,2}, {3,5}}; // OK

    A a1[] = {A{1,2}, A{3,4}}; // OK

    A a2[] = {{1,2}, {3,4}}; // Error

    return 0;
}

24

@StoryTellerと@Sneftelによる優れた回答が主な理由です。ただし、私見では、これは理にかなっています(少なくとも私はそうします)。将来の保証の一環として、後でコードを変更します。あなたの例を考えてみましょう:

class A {
    public:
        explicit A( int b, int c ); 
};

このコードは、から直接恩恵を受けることはありませんexplicit

しばらくして、のデフォルト値を追加することにしたcので、次のようになります。

class A {
    public:
        A( int b, int c=0 ); 
};

これを行うときは、cパラメータに焦点を合わせています。振り返ってみると、デフォルト値が必要です。Aそれ自体を暗黙的に構築する必要があるかどうかに必ずしも焦点を当てているわけではありません。残念ながら、この変更によりexplicit再び関連性が高まります。

したがって、ctorがexplicitであることを伝えるために、最初にメソッドを作成するときにそうすることはお金がかかるかもしれません。


しかし、メンテナがそのデフォルトを追加し、結果変換コンストラクタとして利用可能である必要があると結論付けた場合どうでしょうか?今、彼らはそれが永遠に存在していたことを取り除く必要explicitがあり、テクニカルサポートはその変更についての電話で溢れ、それは単なるノイズであり、それを取り除くことは無害であると説明するのに何時間も費やしexplicitます。個人的には、将来を予測するのはあまり得意ではありません。インターフェイスがどのように見えるかを決めるのは十分に難しいです。
ピートベッカー

@PeteBeckerそれは良い点です。個人的には、2つのケースは非対称であり、パラメーターをデフォルトにする(またはパラメーターを削除する)場合、クラスを誤って暗黙的に構築可能にし、同時に振り返ってみるとそうあるべきであることに実際に気付くのがはるかに一般的だと思います。そうは言っても、これらは「ソフト」な考慮事項であり、人/プロジェクトなどによって異なる場合があります。あるいは単に好みの問題である場合もあります。
アミタボリー2016

8

これがこの議論に対する私の5セントです:

struct Foo {
    Foo(int, double) {}
};

struct Bar {
    explicit Bar(int, double) {}
};

void foo(const Foo&) {}
void bar(const Bar&) {}

int main(int argc, char * argv[]) {
    foo({ 42, 42.42 }); // valid
    bar({ 42, 42.42 }); // invalid
    return 0;
}

簡単にわかるexplicitようにbar、のコンストラクターがstruct Barとして宣言されているため、関数と一緒に初期化子リストを使用できませんexplicit

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.