Tại sao tôi không thể chuyển tiếp khai báo một lớp trong không gian tên bằng dấu hai chấm?


164
class Namespace::Class;

Tại sao tôi phải làm điều này?:

namespace Namespace {
    class Class;
}

Sử dụng VC ++ 8.0, trình biên dịch có vấn đề:

lỗi C2653: 'Không gian tên': không phải là tên lớp hoặc không gian tên

Tôi giả sử rằng vấn đề ở đây là trình biên dịch không thể biết Namespaceđược là một lớp hay một không gian tên? Nhưng tại sao điều này lại quan trọng vì nó chỉ là một tuyên bố chuyển tiếp?

Có cách nào khác để chuyển tiếp khai báo một lớp được định nghĩa trong một số không gian tên không? Cú pháp trên có cảm giác như tôi đang "mở lại" không gian tên và mở rộng định nghĩa của nó. Điều gì xảy ra nếu Classkhông thực sự được định nghĩa trong Namespace? Điều này sẽ dẫn đến một lỗi tại một số điểm?


44
Hãy để tôi không đồng ý với tất cả các câu trả lời ở đây, và nói rằng đó chỉ là một lỗi thiết kế của ngôn ngữ. Họ có thể nghĩ tốt hơn.
Pavel Radzivilovsky

Điều này đang có xu hướng thảo luận về lý do tại sao điều này là bất hợp pháp trong C ++ (mang tính chủ quan) và nó có vẻ tranh luận. Bỏ phiếu để đóng.
David Thornley

7
Làm thế nào trình biên dịch phải biết rằng trong A::Bmột Alà một định danh không gian tên thay vì một tên lớp?
David R Tribble

@STingRaySC: Cuộc thảo luận mang tính chủ quan ở chỗ không có câu trả lời rõ ràng tại sao C ++ làm điều này, vì vậy chúng tôi đang suy đoán. (Câu hỏi là một câu hỏi về shotgun, với một số câu hỏi có câu trả lời khách quan, đã được trả lời.) Vào thời điểm đó, tôi trở nên nhạy cảm với dấu vết của tranh luận, và sự đồng ý của bạn với Pavel rằng đây là một hành vi sai trái của C ++. Tôi không có vấn đề với một câu hỏi tại sao nó quan trọng nếu Namespacelà một lớp hoặc không gian tên. Chỉ cần không nhận được bất cứ nơi nào gần gợi ý về khả năng có thể hình dung bắt đầu một cuộc chiến ngọn lửa ngôn ngữ theo cú pháp.
David Thornley

Câu trả lời:


85

Bởi vì bạn không thể. Trong ngôn ngữ C ++, các tên đủ điều kiện chỉ được sử dụng để chỉ các thực thể hiện có (tức là đã khai báo trước đó). Chúng không thể được sử dụng để giới thiệu các thực thể mới .

Và trên thực tế, bạn đang "mở lại" không gian tên để khai báo các thực thể mới. Nếu Classsau này lớp được định nghĩa là thành viên của không gian tên khác nhau - thì đó là một lớp hoàn toàn khác không liên quan gì đến lớp bạn đã khai báo ở đây.

Khi bạn đến điểm xác định lớp được khai báo trước, bạn không cần phải "mở lại" không gian tên một lần nữa. Bạn có thể định nghĩa nó trong không gian tên toàn cầu (hoặc bất kỳ không gian tên nào kèm theo Namespace) như

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

Vì bạn đang đề cập đến một thực thể đã được khai báo trong không gian tên Namespace, bạn có thể sử dụng tên đủ điều kiện Namespace::Class.


10
@STingRaySC: Cách duy nhất để chuyển tiếp khai báo một lớp lồng là đặt khai báo bên trong định nghĩa của lớp kèm theo. Và thực sự không có cách nào để chuyển tiếp khai báo lớp lồng trước định nghĩa của lớp kèm theo.
AnT

@STingRaySC: Một lớp lồng nhau có thể được khai báo fwd - xem câu trả lời của tôi.
John Dibling

8
@ John Dibling: Lớp lồng nhau là một lớp được khai báo bên trong một lớp khác. Một lớp được khai báo ngay bên trong một không gian tên không phải là một lớp lồng nhau. Không có gì về các lớp sensted trong câu trả lời của bạn.
AnT

198

Bạn đang nhận được câu trả lời đúng, hãy để tôi thử viết lại:

class Namespace::Class;

Tại sao tôi phải làm điều này?

Bạn phải làm điều này bởi vì thuật ngữ Namespace::Classnày đang nói với trình biên dịch:

... OK, trình biên dịch. Đi tìm không gian tên có tên Namespace và trong đó đề cập đến lớp có tên Class.

Nhưng trình biên dịch không biết bạn đang nói về cái gì vì nó không biết bất kỳ không gian tên nào được đặt tên Namespace. Ngay cả khi có một không gian tên được đặt tên Namespace, như trong:

namespace Namespace
{
};

class Namespace::Class;

nó vẫn không hoạt động, bởi vì bạn không thể khai báo một lớp trong một không gian tên từ bên ngoài không gian tên đó. Bạn phải ở trong không gian tên.

Vì vậy, trên thực tế bạn có thể chuyển tiếp khai báo một lớp trong một không gian tên. Chỉ cần làm điều này:

namespace Namespace
{
    class Class;
};

39
Tất cả các câu trả lời khác đều gây nhầm lẫn cho tôi nhưng điều này "bạn không thể khai báo một lớp trong một không gian tên từ bên ngoài không gian tên đó. Bạn phải ở trong không gian tên." là gợi ý rất hữu ích để ghi nhớ.
bảnh bao

22

Tôi cho rằng đó là lý do tương tự mà bạn không thể khai báo các không gian tên lồng nhau trong một lần như thế này:

namespace Company::Communications::Sockets {
}

và bạn phải làm điều này:

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

1
Đây thực sự không phải là một câu trả lời giải thích tại sao bạn không thể làm điều đó.
StarPilot

6
Đây là một câu trả lời giúp tiết kiệm rất nhiều thời gian của tôi
Kadir Erdem Demir

17
C ++ 17 thêm điều này.
rparolin

Ở đây bạn biết rằng tất cả là không gian tên. Nhưng với lớp Company :: Communications :: Socket bạn không biết Truyền thông là không gian tên hay lớp (trong đó socket là lớp lồng nhau).
Lothar

12

Không rõ loại biến khai báo chuyển tiếp thực sự là gì. Tuyên bố phía trước class Namespace::Class;có thể có nghĩa là

namespace Namespace {
  class Class;
}

hoặc là

class Namespace {
public:
  class Class;
};


6
Tôi nghĩ rằng đây là một trong những câu trả lời hay nhất, bởi vì nó trả lời tại sao điều này không thể dễ dàng xác định bởi chính trình biên dịch.
Devolus

1

Có rất nhiều câu trả lời xuất sắc về lý do liên quan đến việc không cho phép nó. Tôi chỉ muốn cung cấp các điều khoản tiêu chuẩn nhàm chán đặc biệt cấm nó. Điều này đúng với C ++ 17 (n4659).

Đoạn trong câu hỏi là [class.name] / 2 :

Một khai báo chỉ bao gồm định danh khóa lớp ; là một khai báo tên trong phạm vi hiện tại hoặc khai báo chuyển tiếp của định danh dưới dạng tên lớp. Nó giới thiệu tên lớp vào phạm vi hiện tại.

Ở trên định nghĩa những gì cấu thành một tuyên bố chuyển tiếp (hoặc khai báo lại của một lớp). Về cơ bản, nó phải là một trong class identifier;, struct identifier;hoặc union identifier;nơi số nhận dạng là định nghĩa từ vựng phổ biến ở [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

Đó là sản xuất của chương trình chung [a-zA-Z_][a-zA-Z0-9_]*mà tất cả chúng ta đều quen thuộc. Như bạn có thể thấy, điều này ngăn cản việc class foo::bar;khai báo chuyển tiếp hợp lệ, vì foo::barkhông phải là định danh. Đó là một cái tên đủ điều kiện, một cái gì đó khác nhau.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.