โปรแกรมที่คอมไพล์แตกต่างกันในคอมไพเลอร์ C ++ หลัก 3 ตัว คนไหนที่ใช่?


116

จากการติดตามผลที่น่าสนใจ (ไม่ใช่เรื่องสำคัญในทางปฏิบัติ) สำหรับคำถามก่อนหน้าของฉัน: เหตุใด C ++ จึงอนุญาตให้เราล้อมรอบชื่อตัวแปรในวงเล็บเมื่อประกาศตัวแปร

ฉันพบว่าการรวมการประกาศในวงเล็บกับคุณสมบัติชื่อคลาสที่ฉีดเข้าด้วยกันอาจนำไปสู่ผลลัพธ์ที่น่าประหลาดใจเกี่ยวกับพฤติกรรมของคอมไพเลอร์

ดูโปรแกรมต่อไปนี้:

#include <iostream>
struct B
{
};

struct C
{
  C (){ std::cout << "C" << '\n'; }
  C (B *) { std::cout << "C (B *)" << '\n';}
};

B *y = nullptr;
int main()
{
  C::C (y);
}
  1. การคอมไพล์ด้วย g ++ 4.9.2 ทำให้ฉันมีข้อผิดพลาดในการคอมไพล์ต่อไปนี้:

    main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
  2. คอมไพล์สำเร็จด้วย MSVC2013 / 2015 และพิมพ์ C (B *)

  3. คอมไพล์สำเร็จด้วย clang 3.5 และพิมพ์ C

คำถามบังคับคือคำถามใดถูกต้อง? :)

(ฉันแกว่งไปทางเวอร์ชันเสียงดังอย่างมากแม้ว่าวิธี msvc จะหยุดการประกาศตัวแปรหลังจากเปลี่ยนประเภทด้วยเทคนิค typedef ดูเหมือนจะแปลก ๆ )


3
C::C y;ไม่สมเหตุสมผลใช่มั้ย? ไม่ไม่C::C (y); ในตอนแรกที่ผมคิดว่านี่คือตัวอย่างของส่วนใหญ่ที่รบกวน-แยกstackoverflow.com/questions/tagged/most-vexing-parseแต่ตอนนี้ฉันคิดว่ามันเป็นเพียงพฤติกรรมที่ไม่ได้กำหนดความหมายของทั้งสามคอมไพเลอร์มี "สิทธิ".
Dale Wilson

4
# 3 เสียงดังผิดแน่นอน # 2 msvc อนุญาตเกินไปและ # 1 g ++ ถูก ((ฉันเดา)

8
C::Cไม่ได้ตั้งชื่อประเภทมันตั้งชื่อฟังก์ชันดังนั้น GCC จึงถูกต้อง imo
Galik


11
ยื่นเป็นข้อผิดพลาดดังกราว
Cornstalks

คำตอบ:


91

GCC ถูกต้องอย่างน้อยตามกฎการค้นหา C ++ 11 3.4.3.1 [class.qual] / 2 ระบุว่าถ้าตัวระบุชื่อที่ซ้อนกันเหมือนกับชื่อคลาสจะอ้างถึงตัวสร้างที่ไม่ใช่ชื่อคลาสที่ฉีดเข้าไป ยกตัวอย่าง:

B::A ba;           // object of type A
A::A a;            // error, A::A is not a type name
struct A::A a2;    // object of type A

ดูเหมือนว่า MSVC จะตีความผิดว่าเป็นนิพจน์คาสต์สไตล์ฟังก์ชันที่สร้างพารามิเตอร์ชั่วคราวCด้วยyเป็นคอนสตรัคเตอร์ และเสียงดังกราวแปลความหมายผิดว่ามันเป็นคำประกาศของตัวแปรที่เรียกว่าประเภทyC


2
ใช่ 3.4.3.1/2 คือกุญแจสำคัญ เก่งมาก!
Lightness Races ใน Orbit

มันขึ้นว่า "ในการค้นหาที่ไม่สนใจชื่อฟังก์ชัน" สำหรับฉันแล้วดูเหมือนว่าในตัวอย่างที่ให้มาโดยเฉพาะA::A a;ชื่อฟังก์ชันควรละเว้น - หรือไม่?
Columbo

1
ตามตัวเลขใน N4296 คีย์คือ 3.4.3.1/2.1 จริงๆ: "ถ้าชื่อที่ระบุไว้หลังตัวระบุชื่อซ้อนเมื่อค้นหาใน C คือชื่อคลาสที่ถูกแทรกของ C [... ] ชื่อจะถูกพิจารณาแทนเพื่อตั้งชื่อตัวสร้างของคลาส C " สรุปของ Mike นั้นง่ายเกินไปเล็กน้อยตัวอย่างเช่น typedef ของชื่อคลาสภายในคลาสจะอนุญาตให้ตัวระบุชื่อที่ซ้อนกันซึ่งแตกต่างจากชื่อคลาสยังคงอ้างถึงชื่อคลาสดังนั้นจึงยังคงอ้างถึง ctor
Jerry Coffin

2
@Mgetz: จากคำถาม: "มันรวบรวมประสบความสำเร็จกับ MSVC2013 / 2015 และพิมพ์C (B *) "
Lightness Races ใน Orbit

2
เพื่อความสมบูรณ์ควรชี้แจงว่ามีรูปแบบที่ไม่ถูกต้องด้วยการวินิจฉัยที่จำเป็นหรือรูปแบบที่ไม่ดีโดยไม่จำเป็นต้องวินิจฉัย ถ้าเป็นอย่างหลังคอมไพเลอร์ทั้งหมดก็ "ถูกต้อง"
MM

16

G ++ ถูกต้องเนื่องจากมีข้อผิดพลาด เนื่องจากไม่สามารถเรียกตัวสร้างได้โดยตรงในรูปแบบดังกล่าวหากไม่มีnewตัวดำเนินการ และแม้ว่ารหัสของคุณจะเรียกC::Cแต่ดูเหมือนว่าเป็นการเรียกตัวสร้าง อย่างไรก็ตามตามมาตรฐาน C ++ 11 3.4.3.1 นี่ไม่ใช่การเรียกใช้ฟังก์ชันทางกฎหมายหรือชื่อประเภท ( ดูคำตอบของ Mike Seymour )

เสียงดังไม่ถูกต้องเนื่องจากไม่ได้เรียกใช้ฟังก์ชันที่ถูกต้อง

MSVC เป็นสิ่งที่สมเหตุสมผล แต่ก็ยังไม่เป็นไปตามมาตรฐาน


2
อะไร newเปลี่ยนแปลงผู้ประกอบการ?
Neil Kirk

1
@NeilKirk: จำนวนมากสำหรับคนที่คิดว่าnew B(1,2,3)เป็นชนิดของ "โทรนวกรรมิกโดยตรง" บางคน (ซึ่งแน่นอนมันไม่ได้) ที่แตกต่างจากการเริ่มชั่วคราวหรือการประกาศB(1,2,3) B b(1,2,3)
Lightness Races ใน Orbit

@LightningRacisinObrit คุณจะอธิบายว่าอะไรnew B(1,2,3)คืออะไร?
user2030677

1
@ user2030677: นิพจน์ใหม่โดยใช้คีย์เวิร์ดnewชื่อชนิดและรายการอาร์กิวเมนต์ตัวสร้าง ยังไม่ใช่ "การเรียกผู้สร้างโดยตรง"
Lightness Races ใน Orbit

"เสียงดังผิดเพราะมันไม่ได้เรียกใช้ฟังก์ชันที่ถูกต้อง": ฉันคิดว่า (เพราะคำพูดของ OP เกี่ยวกับวงเล็บในการประกาศ) ที่เสียงดังแปลC::C (y); ว่าC::C y;คือการกำหนดตัวแปร y ประเภท C (โดยใช้ชนิดที่ฉีด C: : C ในขณะที่ละเลยข้อกำหนดภาษาที่บ้าขึ้นเรื่อย ๆ ของ 3.4.1,2 ซึ่งทำให้ C :: C เป็นตัวสร้าง) นั่นไม่ใช่ข้อผิดพลาดที่ชัดเจนอย่างที่คุณคิด imo
Peter - Reinstate Monica
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.