Mengapa saya tidak bisa meneruskan-mendeklarasikan kelas di namespace menggunakan titik dua?

164
class Namespace::Class;

Mengapa saya harus melakukan ini ?:

namespace Namespace {
    class Class;
}

Menggunakan VC ++ 8.0, masalah kompiler:

kesalahan C2653: 'Namespace': bukan nama kelas atau namespace

Saya berasumsi bahwa masalahnya di sini adalah bahwa kompiler tidak dapat menentukan apakah Namespacekelas atau namespace? Tapi mengapa ini penting karena itu hanya deklarasi maju?

Apakah ada cara lain untuk meneruskan-mendeklarasikan kelas yang didefinisikan dalam beberapa namespace? Sintaks di atas terasa seperti saya "membuka kembali" namespace dan memperluas definisinya. Bagaimana jika Classsebenarnya tidak didefinisikan dalam Namespace? Apakah ini akan menghasilkan kesalahan di beberapa titik?

Yong Li
sumber
44
Biarkan saya tidak setuju dengan semua jawaban di sini, dan katakan itu hanya bug desain dari bahasa tersebut. Mereka bisa berpikir lebih baik.
Pavel Radzivilovsky
Ini cenderung ke arah diskusi mengapa ini ilegal di C ++ (yang subjektif), dan terlihat argumentatif. Voting untuk ditutup.
David Thornley
7
Bagaimana compiler seharusnya tahu bahwa di A::Bdalam Aadalah identifier namespace bukan nama kelas?
David R Tribble
@STingRaySC: Diskusi bersifat subyektif karena tidak ada jawaban yang jelas mengapa C ++ melakukan ini, jadi kami berspekulasi. (Pertanyaannya adalah pertanyaan senapan, dengan beberapa pertanyaan dengan jawaban obyektif, yang sudah dijawab sebelumnya.) Pada saat itu, saya menjadi peka terhadap jejak argumen, dan persetujuan Anda dengan Pavel bahwa ini adalah kesalahpahaman kualifikasi C ++. Saya tidak punya masalah dengan pertanyaan mengapa itu penting jika Namespacekelas atau namespace. Hanya saja, jangan mendekati petunjuk kemungkinan memulai perang api bahasa atas sintaksis.
David Thornley

Jawaban:

85

Karena kamu tidak bisa. Dalam bahasa C ++ nama yang sepenuhnya memenuhi syarat hanya digunakan untuk merujuk entitas yang ada (yaitu yang sebelumnya dinyatakan). Mereka tidak dapat digunakan untuk memperkenalkan entitas baru .

Dan Anda berada pada kenyataannya "membuka kembali" namespace untuk menyatakan entitas baru. Jika kelas Classnanti didefinisikan sebagai anggota namespace yang berbeda - itu adalah kelas yang sama sekali berbeda yang tidak ada hubungannya dengan yang Anda nyatakan di sini.

Setelah Anda sampai pada titik mendefinisikan kelas pra-deklarasi, Anda tidak perlu "membuka kembali" namespace lagi. Anda dapat mendefinisikannya di namespace global (atau namespace yang melampirkan Anda Namespace) sebagai

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

Karena Anda merujuk ke entitas yang telah dideklarasikan di namespace Namespace, Anda dapat menggunakan nama yang memenuhi syarat Namespace::Class.

Semut
sumber
10
@STingRaySC: Satu-satunya cara untuk meneruskan-mendeklarasikan kelas bertingkat adalah dengan meletakkan deklarasi di dalam definisi kelas terlampir. Dan memang tidak ada cara untuk meneruskan-mendeklarasikan kelas bersarang sebelum definisi kelas terlampir.
AnT
@STingRaySC: Kelas bersarang dapat dideklarasikan - lihat jawaban saya.
John Dibling
8
@ John Dibling: Kelas bersarang adalah kelas yang dideklarasikan di dalam kelas lain. Kelas yang dideklarasikan langsung di dalam namespace bukan kelas bersarang. Tidak ada tentang kelas sensted dalam jawaban Anda.
AnT
198

Anda mendapatkan jawaban yang benar, izinkan saya mencoba kembali kata-kata:

class Namespace::Class;

Mengapa saya harus melakukan ini?

Anda harus melakukan ini karena istilahnya Namespace::Classmemberitahu compiler:

... OK, kompiler. Pergi menemukan namespace bernama Namespace, dan di dalamnya merujuk ke kelas bernama Kelas.

Tetapi kompiler tidak tahu apa yang Anda bicarakan karena tidak tahu nama namespace Namespace. Bahkan jika ada namespace bernama Namespace, seperti pada:

namespace Namespace
{
};

class Namespace::Class;

itu masih tidak akan berhasil, karena Anda tidak dapat mendeklarasikan kelas di dalam namespace dari luar namespace itu. Anda harus berada di namespace.

Jadi, Anda sebenarnya bisa maju mendeklarasikan kelas dalam namespace. Lakukan saja ini:

namespace Namespace
{
    class Class;
};
John Dibling
sumber
39
Semua jawaban lain membingungkan saya, tetapi ini "Anda tidak bisa mendeklarasikan kelas di dalam namespace dari luar namespace itu. Anda harus berada di namespace." adalah petunjuk yang sangat membantu untuk diingat.
cepat
22

Saya kira itu karena alasan yang sama Anda tidak dapat mendeklarasikan ruang nama bersarang dalam satu cara seperti ini:

namespace Company::Communications::Sockets {
}

dan Anda harus melakukan ini:

namespace Company {
  namespace Communications {
    namespace Sockets {
    }
  }
}
Igor Zevaka
sumber
1
Ini sebenarnya bukan jawaban yang menjelaskan mengapa Anda tidak bisa melakukannya.
StarPilot
6
Ini adalah jawaban yang menghemat banyak waktu saya
Kadir Erdem Demir
17
C ++ 17 menambahkan ini.
rparolin
Di sini Anda tahu bahwa semua adalah ruang nama. Tetapi dengan kelas Company :: Communications :: Socket Anda tidak tahu apakah Communications adalah namespace atau kelas (di mana socket adalah kelas bersarang).
Lothar
12

Tidak akan jelas apa sebenarnya tipe variabel yang dinyatakan maju. Deklarasi maju class Namespace::Class;bisa berarti

namespace Namespace {
  class Class;
}

atau

class Namespace {
public:
  class Class;
};
Martin G
sumber
6
Saya pikir ini adalah salah satu jawaban terbaik, karena itu menjawab mengapa ini tidak dapat dengan mudah ditentukan oleh kompiler itu sendiri.
Devolus
1

Ada banyak jawaban bagus tentang alasan yang digunakan untuk menolaknya. Saya hanya ingin memberikan klausa standar yang membosankan yang secara khusus melarangnya. Ini berlaku untuk C ++ 17 (n4659).

Paragraf yang dimaksud adalah [class.name] / 2 :

Deklarasi yang hanya terdiri dari pengidentifikasi kunci kelas ; adalah deklarasi ulang nama dalam lingkup saat ini atau deklarasi maju dari pengidentifikasi sebagai nama kelas. Ini memperkenalkan nama kelas ke dalam lingkup saat ini.

Di atas mendefinisikan apa yang merupakan deklarasi maju (atau redclaration dari suatu kelas). Pada dasarnya, itu harus salah satu dari class identifier;, struct identifier;atau di union identifier;mana identifer adalah definisi leksikal yang umum di [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

Yang merupakan produksi dari skema umum yang [a-zA-Z_][a-zA-Z0-9_]*kita semua kenal. Seperti yang Anda lihat, ini menghalangi class foo::bar;untuk menjadi deklarasi maju yang valid, karena foo::barbukan pengenal. Itu nama yang sepenuhnya memenuhi syarat, sesuatu yang berbeda.

StoryTeller - Unslander Monica
sumber