ตัวสร้างที่ชัดเจนรับอาร์กิวเมนต์หลายตัว


88

การสร้างคอนสตรัคเตอร์ที่มีหลายอาร์กิวเมนต์explicitมีผล (มีประโยชน์) หรือไม่?

ตัวอย่าง:

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

คำตอบ:


120

จนถึง C ++ 11 ใช่ไม่มีเหตุผลที่จะใช้explicitกับตัวสร้างหลายอาร์กิวเมนต์

ที่เปลี่ยนแปลงใน C ++ 11 เนื่องจากรายการ initializer โดยพื้นฐานแล้วการเริ่มต้นการคัดลอก (แต่ไม่ใช่การเริ่มต้นโดยตรง) ด้วยรายการตัวเริ่มต้นจำเป็นต้องไม่ทำเครื่องหมาย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แต่เป็นเพียงเพราะมันเป็นส่วนขยายของตรรกะของความหมายที่มีอยู่สำหรับ ฉันจะไม่รบกวนการสร้าง multi-arg constructor เป็นการ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 คือเหตุผลหลัก อย่างไรก็ตาม IMHO นี่ก็สมเหตุสมผลแล้ว (อย่างน้อยฉันก็ทำ) เนื่องจากเป็นส่วนหนึ่งของการพิสูจน์อักษรในอนาคตจะเปลี่ยนรหัสในภายหลัง พิจารณาตัวอย่างของคุณ:

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เป็นเพียงเสียงรบกวนและการลบออกนั้นไม่เป็นอันตราย โดยส่วนตัวแล้วฉันไม่ค่อยถนัดในการทำนายอนาคต ก็พอที่ยากที่จะตัดสินใจว่าอินเตอร์เฟซที่ควรมีลักษณะเช่นนี้
Pete Becker

@PeteBecker นั่นเป็นจุดที่ดี โดยส่วนตัวแล้วฉันคิดว่าทั้งสองกรณีไม่สมมาตรและเป็นเรื่องปกติมากเมื่อตั้งค่าพารามิเตอร์เริ่มต้น (หรือลบออก) เพื่อทำให้ชั้นเรียนสร้างขึ้นโดยปริยายโดยไม่ได้ตั้งใจจากนั้นจึงตระหนักในเวลาเดียวกันว่าควรจะย้อนกลับไปในเวลาเดียวกัน ดังที่กล่าวมานี้เป็นข้อพิจารณาที่ "นุ่มนวล" และอาจแตกต่างกันไประหว่างผู้คน / โครงการ / ฯลฯ หรือแม้กระทั่งเรื่องของรสนิยม
Ami Tavory

8

นี่คือห้าเซ็นต์ของฉันสำหรับการสนทนานี้:

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.