เหตุใดฉันจึงไม่สามารถประกาศคลาสในเนมสเปซโดยใช้ double colons ได้


164
class Namespace::Class;

ทำไมฉันต้องทำสิ่งนี้:

namespace Namespace {
    class Class;
}

การใช้ VC ++ 8.0 ปัญหาเกี่ยวกับคอมไพเลอร์:

ข้อผิดพลาด C2653: 'Namespace': ไม่ใช่ชื่อคลาสหรือเนมสเปซ

ฉันคิดว่าปัญหาที่นี่คือคอมไพเลอร์ไม่สามารถบอกได้ว่าNamespaceเป็นคลาสหรือ namespace หรือไม่? แต่ทำไมเรื่องนี้ถึงเกิดขึ้นเพราะมันเป็นเพียงการประกาศล่วงหน้า

มีวิธีการส่งต่อประกาศคลาสที่กำหนดไว้ในบาง namespace หรือไม่ ไวยากรณ์ข้างต้นรู้สึกเหมือนฉัน "เปิดใหม่" namespace และขยายคำจำกัดความ เกิดอะไรขึ้นถ้าClassไม่ได้นิยามไว้จริง ๆNamespace? บางครั้งจะส่งผลให้เกิดข้อผิดพลาดหรือไม่?


44
ให้ฉันไม่เห็นด้วยกับคำตอบทั้งหมดที่นี่และบอกว่ามันเป็นเพียงข้อบกพร่องในการออกแบบของภาษา พวกเขาสามารถคิดได้ดีขึ้น
Pavel Radzivilovsky

นี่เป็นการถกเถียงกันว่าทำไมเรื่องนี้ถึงผิดกฎหมายใน C ++ (ซึ่งเป็นเรื่องส่วนตัว) และมันก็เป็นเรื่องที่ถกเถียงกัน การลงคะแนนให้ปิด
David Thornley

7
คอมไพเลอร์ควรจะรู้ได้อย่างไรว่าในA::Bthe Aเป็นตัวระบุเนมสเปซแทนที่จะเป็นชื่อคลาส?
David R Tribble

@STINGRaySC: การสนทนาเป็นเรื่องส่วนตัวที่ไม่มีคำตอบที่ชัดเจนว่าทำไม C ++ ทำเช่นนี้ดังนั้นเราจึงคาดเดา (คำถามนี้เป็นคำถามปืนลูกซองโดยมีบางคำถามที่มีคำตอบวัตถุประสงค์ซึ่งได้รับคำตอบแล้ว) ณ จุดนั้นฉันมีความอ่อนไหวต่อร่องรอยของการโต้แย้งและข้อตกลงของคุณกับ Pavel ว่านี่เป็นคุณสมบัติที่ผิดของ C ++ ฉันไม่มีปัญหากับคำถามที่ว่าทำไมมันถึงมีความสำคัญหากNamespaceเป็นคลาสหรือเนมสเปซ อย่าเพิ่งเข้าไปใกล้คำใบ้ของความเป็นไปได้ที่จะเริ่มสงครามเปลวไฟเหนือไวยากรณ์
David Thornley

คำตอบ:


85

เพราะคุณทำไม่ได้ ในชื่อที่ผ่านการรับรองภาษา C ++ จะใช้เพื่ออ้างถึงเอนทิตีที่มีอยู่ (เช่นประกาศก่อนหน้านี้) ไม่สามารถใช้เพื่อแนะนำเอนทิตีใหม่ได้

และคุณอยู่ในความเป็นจริง "เปิดใหม่" namespace เพื่อประกาศหน่วยงานใหม่ ถ้าคลาสClassนั้นถูกกำหนดเป็นสมาชิกของเนมสเปซที่แตกต่างกันในภายหลัง - คลาสนั้นแตกต่างกันโดยสิ้นเชิงซึ่งไม่เกี่ยวข้องกับสิ่งที่คุณประกาศที่นี่

เมื่อคุณไปถึงจุดที่กำหนดคลาสที่ประกาศล่วงหน้าคุณไม่จำเป็นต้อง "เปิดใหม่" เนมสเปซอีกครั้ง คุณสามารถกำหนดไว้ในเนมสเปซส่วนกลาง (หรือเนมสเปซที่ล้อมรอบคุณNamespace) เป็น

class Namespace::Class {
  /* whatever */
};

เนื่องจากคุณอ้างถึงเอนทิตีที่ได้รับการประกาศในเนมสเปซNamespaceแล้วคุณสามารถใช้ชื่อที่ผ่านการรับรองNamespace::Classได้


10
@STingRaySC: วิธีเดียวที่จะส่งต่อประกาศคลาสที่ซ้อนอยู่คือการวางการประกาศภายในคำจำกัดความของคลาสที่ล้อมรอบ และไม่มีทางที่จะส่งต่อ - ประกาศคลาสที่ซ้อนกันก่อนนิยามของคลาสที่ล้อมรอบ
An

@STINGRaySC: สามารถประกาศคลาสที่ซ้อนกันได้ - ดูคำตอบของฉัน
John Dibling

8
@John Dibling: คลาสแบบซ้อนเป็นคลาสที่ประกาศในคลาสอื่น คลาสที่ประกาศทันทีภายในเนมสเปซไม่ใช่คลาสที่ซ้อนกัน ไม่มีคำตอบเกี่ยวกับชั้นเรียนของคุณ
An

198

คุณได้รับคำตอบที่ถูกต้องขอฉันลองใหม่อีกครั้งว่า:

class Namespace::Class;

ทำไมฉันต้องทำเช่นนี้?

คุณต้องทำสิ่งนี้เพราะคำศัพท์Namespace::Classนี้บอกกับผู้แปล:

... ตกลงคอมไพเลอร์ ไปหาเนมสเปซที่มีชื่อว่าเนมสเปซและภายในที่อ้างถึงคลาสที่ชื่อคลาส

แต่คอมไพเลอร์ไม่ได้รู้ว่าสิ่งที่คุณกำลังพูดถึงเพราะมันไม่ได้รู้ namespace Namespaceใดชื่อ แม้ว่าจะมีเนมสเปซตั้งชื่อNamespaceเช่นเดียวกับใน:

namespace Namespace
{
};

class Namespace::Class;

มันยังคงไม่ทำงานเพราะคุณไม่สามารถประกาศคลาสภายในเนมสเปซจากภายนอกเนมสเปซนั้นได้ คุณต้องอยู่ในเนมสเปซ

ดังนั้นคุณสามารถประกาศคลาสในเนมสเปซได้ แค่ทำสิ่งนี้:

namespace Namespace
{
    class Class;
};

39
คำตอบอื่น ๆ ทั้งหมดทำให้ฉันสับสน แต่นี่ "คุณไม่สามารถประกาศคลาสภายในเนมสเปซจากภายนอกเนมสเปซนั้นได้คุณต้องอยู่ในเนมสเปซ" คำใบ้ที่เป็นประโยชน์มากที่จะจำ
dashesy

22

ฉันคิดว่ามันเป็นเพราะเหตุผลเดียวกับที่คุณไม่สามารถประกาศเนมสเปซซ้อนในคราวเดียว:

namespace Company::Communications::Sockets {
}

และคุณต้องทำสิ่งนี้:

namespace Company {
  namespace Communications {
    namespace Sockets {
    }
  }
}

1
นี่ไม่ใช่คำตอบที่อธิบายว่าทำไมคุณไม่สามารถทำได้
StarPilot

6
นี่เป็นคำตอบที่ช่วยประหยัดเวลาของฉันได้มาก
Kadir Erdem Demir

17
C ++ 17 เพิ่มสิ่งนี้
rparolin

ที่นี่คุณรู้ว่าทุกคนเป็น namespaces แต่ด้วยคลาส บริษัท :: การสื่อสาร :: ซอคเก็ตคุณไม่ทราบว่าการสื่อสารเป็นเนมสเปซหรือคลาส (โดยที่ซ็อกเก็ตเป็นคลาสที่ซ้อนอยู่)
Lothar

12

มันจะไม่ชัดเจนว่าชนิดของตัวแปรที่ประกาศล่วงหน้าคืออะไร การประกาศล่วงหน้าclass Namespace::Class;อาจหมายถึง

namespace Namespace {
  class Class;
}

หรือ

class Namespace {
public:
  class Class;
};


6
ฉันคิดว่านี่เป็นหนึ่งในคำตอบที่ดีที่สุดเพราะมันเป็นคำตอบว่าทำไมคอมไพเลอร์จึงไม่สามารถกำหนดได้โดยง่าย
Devolus

1

มีคำตอบที่ยอดเยี่ยมมากมายเกี่ยวกับเหตุผลที่เกี่ยวข้องกับการไม่อนุญาต ฉันแค่ต้องการให้ประโยคมาตรฐานที่น่าเบื่อโดยเฉพาะห้ามมัน สิ่งนี้ถือเป็นจริงสำหรับ C ++ 17 (n4659)

ย่อหน้าที่เป็นปัญหาคือ[class.name] / 2 :

การประกาศประกอบด้วยตัวระบุคลาสคีย์เท่านั้น เป็นการประกาศชื่อในขอบเขตปัจจุบันหรือการประกาศไปข้างหน้าของตัวระบุว่าเป็นชื่อคลาส มันแนะนำชื่อคลาสในขอบเขตปัจจุบัน

ข้างต้นกำหนดสิ่งที่ถือเป็นการประกาศไปข้างหน้า (หรือ redclaration ของชั้นเรียน) โดยพื้นฐานแล้วมันต้องเป็นหนึ่งclass identifier;, struct identifier;หรือunion identifier;ที่ตัวบ่งชี้เป็นคำนิยามคำศัพท์ที่พบบ่อยใน[lex.name] :

identifier:
  identifier-nondigit
  identifier identifier-nondigit
  identifier digit
identifier-nondigit:
  nondigit
  universal-character-name
nondigit: one of
  a b c d e f g h i j k l m
  n o p q r s t u v w x y z
  A B C D E F G H I J K L M
  N O P Q R S T U V W X Y Z _
digit: one of
  0 1 2 3 4 5 6 7 8 9

ซึ่งเป็นการผลิตของรูปแบบทั่วไปที่[a-zA-Z_][a-zA-Z0-9_]*เราทุกคนคุ้นเคย อย่างที่คุณเห็นสิ่งนี้จะขัดขวางไม่ให้class foo::bar;มีการประกาศการส่งต่อที่ถูกต้องเพราะfoo::barไม่ใช่ตัวบ่งชี้ มันเป็นชื่อที่ผ่านการรับรองบางอย่างแตกต่างกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.