2016-03-10 53 views
17

下面一段簡單的代碼編譯,但我不明白爲什麼:C++轉發聲明類中的類

class C { 
    class B; 

    class A { 
     B getB() { return B(); } 
    }; 

    class B { 
    }; 
}; 

int main(int, char**) 
{ 
    return 0; 
} 

如果我再註釋掉「class C」的東西,這樣的B向前聲明的A的定義和B定義不再嵌套類中,代碼不編譯,因爲B是一個不完整的類型:

main.cpp: In member function 'B A::getB()': 
main.cpp:6: error: return type 'struct B' is incomplete 
main.cpp:6: error: invalid use of incomplete type 'struct B' 
main.cpp:3: error: forward declaration of 'struct B' 

我underst以及類型不完整意味着什麼,即它尚未被定義,因此編譯器不可能知道爲它分配多少空間。但爲什麼B在上面的代碼中不被認爲是不完整的,其中AB都是在C內部聲明和定義的?

回答

10

我相信這是[basic.scope.class]結果:

在一個類中聲明的名稱的潛在範圍不僅包括按照 名稱的點聲明的聲明區,也是所有功能體,默認參數例外規範該類中的非靜態數據成員的括號或相等初始值設定項(包括嵌套的 類中的此類事物)。

即,B充分聲明的範圍包括嵌套類的成員函數的主體:

class C { 
    class B; // (1) 

    class A { 
     B getB() { 
      return B(); // both (1) and (2) in scope here 
         // since (2) is the complete type declaration, 
         // this is perfectly fine 
     } 
    }; 

    class B { // (2) 
    }; 
}; 

通過比較,如果C是類的一個命名空間而不是,B類的完整聲明的範圍不會延伸到A::getB()。唯一可見的聲明是B的前向聲明,我標記爲(1) - 因此B()將構建一個不完整的類型。

+0

太棒了,謝謝你清理那個。引發這個問題的是libpqxx的['result'](http://pqxx.org/devprojects/libpqxx/doc/stable/html/Reference/)類,它有嵌套的'tuple'和'field'類,其中'tuple'在它的一個函數體中構造了一個'field'(即第183行) – villapx

5

標準明確規定方法的主體在包含它的類後解釋。

因此,在評估主體C::A::getB(),A, BC都是完整的類型。

+0

任何機會,你可能知道我在哪裏可以找到那句話? – villapx

+1

試圖找到它......不幸的是,在2015草案標準PDF中提到了582次「成員函數」一詞... http://open-std.org/JTC1/SC22/WG21/docs/papers/2015 /n4527.pdf –

9

內聯成員函數的主體直到類定義完全處理完成纔會被處理。

因此,你可以使用:

class A 
{ 
    class B; 
    B getB() { return B(); } 

    class B {}; 
}; 

這也讓那些尚未宣佈在聯成員函數的定義中使用的成員變量。

class Foo 
{ 
    int getBar() { return bar; } 

    int bar; 
}; 

我猜測相同的邏輯擴展到內聯的嵌套類的成員函數定義 - 即它們不是處理,直到含有類定義被完全處理。

PS我無法在標準中快速找到可以驗證我的說明的參考。

PS 2The answer by Barry在標準中有引用使問題中的代碼有效。

+0

你擊敗了我的答案。我試圖找到這些站點。我認爲它是由*支持的。類似地,在名稱查找期間,當用於類X的成員函數的定義中使用的非限定id(5.1)解析爲靜態成員,枚舉器或嵌套類型的類X或 X的基類,將非限定id轉換爲限定符(5.1),其中嵌套名稱說明符 命名成員函數的類。* from ** [class.mfct.non-static] **。如果你同意繼續使用它。 – NathanOliver

+0

@NathanOliver我認爲這是不對的。這只是說'B'會被找到 - 但是'B'會被發現,無論是由於前向聲明。我不知道正確的部分在哪裏,也找不到它。 – Barry

+0

可能*如果類X在名稱空間範圍內定義,則嵌套類Y可以在類X中聲明,稍後在類X的定義中定義或稍後在包含類X的定義的名稱空間範圍中定義。 ** [class.nest] ** – NathanOliver

0

而且,當我有需要轉發聲明嵌套類我傾向於聞到一些不好的設計在我的代碼,我使用的伎倆是:

// Foo.h 
class Foo { 
    class Bar { 
    }; 
}; 
class Foobar : public Foo::Bar {}; 


// Zoo.h 
/* Fwd declare */ 
class FooBar; 
+0

你可以編輯你的答案 – villapx