Konstruktor eksplisit mengambil banyak argumen

88

Apakah membuat konstruktor yang memiliki banyak argumen explicitmemiliki efek (berguna)?

Contoh:

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

Jawaban:

120

Hingga C ++ 11, ya, tidak ada alasan untuk digunakan explicitpada konstruktor multi-argumen.

Itu berubah di C ++ 11, karena daftar penginisialisasi. Pada dasarnya, copy-inisialisasi (tetapi bukan inisialisasi langsung) dengan daftar penginisialisasi mengharuskan konstruktor tidak ditandai explicit.

Contoh:

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
Sneftel
sumber
5
Saya pikir jawaban ini akan lebih baik dengan penjelasan "Mengapa saya menginginkannya" atau "Kapan ini berguna".
MateuszL
@MateuszL Jawaban Edgar mungkin memberikan argumen terbaik mengapa hal itu mungkin berguna (dan bisa dibilang pantas dicentang). Alasannya ada di sana , hanya karena itu adalah perpanjangan logis dari semantik yang ada untuk explicit. Saya pribadi tidak akan repot-repot membuat konstruktor multi-argumen explicit.
Sneftel
31

Anda akan menemukannya untuk inisialisasi brace (misalnya dalam array)

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;
}
StoryTeller - Unslander Monica
sumber
24

Jawaban luar biasa dari @StoryTeller dan @Sneftel adalah alasan utamanya. Namun, IMHO, ini masuk akal (setidaknya saya melakukannya), sebagai bagian dari pembuktian masa depan perubahan kode. Pertimbangkan contoh Anda:

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

Kode ini tidak mendapatkan keuntungan secara langsung explicit.

Beberapa waktu kemudian, Anda memutuskan untuk menambahkan nilai default c, sehingga menjadi seperti ini:

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

Saat melakukan ini, Anda memusatkan perhatian pada cparameter - dalam retrospeksi, itu harus memiliki nilai default. Anda tidak perlu fokus pada apakah Aitu sendiri harus dibangun secara implisit. Sayangnya, perubahan ini membuat explicitrelevan kembali.

Jadi, untuk menyampaikan bahwa seorang ctor explicit, mungkin ada gunanya melakukannya saat pertama kali menulis metode.

Ami Tavory
sumber
Tetapi bagaimana dengan kasus ketika pengelola menambahkan default tersebut dan menyimpulkan bahwa hasilnya harus tersedia sebagai konstruktor pengubah? Sekarang mereka harus menghapus apa explicityang sudah ada di sana selamanya, dan dukungan teknis akan dibanjiri dengan panggilan tentang perubahan itu dan menghabiskan berjam - jam menjelaskan bahwa explicititu hanya kebisingan, dan menghapusnya tidak berbahaya. Secara pribadi, saya tidak pandai memprediksi masa depan; cukup sulit untuk memutuskan seperti apa tampilan antarmuka sekarang .
Pete Becker
@ PeteTee Itu poin yang bagus. Saya pribadi berpikir bahwa kedua kasus itu asimetris, dan itu jauh lebih umum ketika membuat parameter default (atau menghapusnya) untuk secara tidak sengaja membuat kelas secara implisit dapat dibangun, kemudian untuk benar-benar menyadari pada saat yang sama bahwa dalam retrospeksi seharusnya demikian. Meskipun demikian, ini adalah pertimbangan "lunak", dan mungkin berbeda antara orang / proyek / dll., Atau bahkan hanya masalah selera.
Ami Tavory
8

Inilah lima sen saya untuk diskusi ini:

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;
}

Seperti yang dapat Anda lihat dengan mudah, explicitmencegah dari menggunakan daftar penginisialisasi bersama dengan barfungsi bacause konstruktor struct Bardideklarasikan sebagai explicit.

Edgar Rokjān
sumber