2010-08-23 150 views
7

下面是純粹學術發明的類層次結構。預期的行爲是什麼?

struct X{ 
     void f1(); 
     void f2(); 
     void f3(); 
}; 

struct Y : private X{ 
     void f4(); 
}; 

struct Z : X{ 
}; 

struct D : Y, Z{ 
     using X::f2; 
     using Z::X::f3; 
}; 

int main(){} 

我預期使用聲明X :: F2不明確爲 'X' 是 'd'(visbility VS X的可接近)的一個不明確的基礎。然而g ++(ideone.com)很好地編譯它。

我使用Online Comeau進行了檢查,發現X :: f2的聲明出現錯誤。但是,它也爲Z :: X :: f3使用聲明提供了不明確的地方。

那麼預期的行爲是什麼?

編輯1:

到標準的相應部分的引用將是有益的,請。

編輯2:

我與2010年VS檢查,它只有與使用聲明X :: F2異議。然而,這不是關於'X'的歧義(例如gcc和Comeau)。這是關於「錯誤C2876:'X':並非所有重載都可訪問」。

編輯3:

struct X{ 
    void f(){} 
}; 

struct Y : X{ 
    struct trouble{ 
     void f(){} 
    }; 

}; 

struct trouble : X{ 
}; 

struct letscheck : Y, trouble{ 
    using trouble::f; 
}; 

int main(){} 

在這裏,我試圖(故意)與使用聲明類型創建一個問題。海灣合作委員會仍然編譯這個罰款,VS2010也是如此。 Comeau仍然給出了錯誤(如預期的)關於模棱兩可的類型'麻煩'。按照初步查詢的解釋,看來GCC和VS2010是錯誤的。那是對的嗎?

+0

在D添加一個方法,調用f2()看看會發生什麼。 – 2010-08-23 15:00:18

回答

2

我不認爲這些都是不合格的。首先,對於using X::f2,X被查找,並且這將毫無疑義地產生類型X。然後f2X被擡起,這也是明確的(它不在D查找!)。

第二種情況將出於同樣的原因。

但是,如果你通話f2一個D對象上,呼叫將被曖昧,因爲名稱f2是在XD所有子對象擡頭一看,D有兩個這樣的子對象,並f2是一種非靜態成員函數。第二種情況同樣如此。無論您是直接使用Z::X還是X來命名f3,這都沒有什麼不同。這兩者都指定類別X

爲了明確使用聲明,您需要以不同的方式寫入。請注意,在C++ 0x using ThisClass::...;無效。不過在C++ 03中,只要全名是指基類成員。相反,如果在C++ 0x中允許這樣做,則整個使用聲明也將是有效的,因爲C++ 0x不會將子對象考慮在名稱查找中:D::f2明確指的是隻有一個聲明X中的那個)。見DR #39和最終論文N1626

struct D : Y, Z{ 
    // ambiguous: f2 is declared in X, and X is a an ambiguous base class 
    using D::f2; 

    // still fine (if not referred to by calls/etc) :) 
    using Z::X::f3; 
}; 

struct E : D { 
    // ambiguous in C++03 
    // fine in C++0x (if not referred to by an object-context (such as a call)). 
    using D::f2; 
}; 

的C++標準03介紹了本段10.23.4.3.1。對於EDIT3


響應:

是,GCC和VS2010是錯誤的。 trouble是指通過注入的類名稱::trouble找到的類型以及找到的嵌套類Y::trouble。名稱trouble::之前使用非限定查找(由3.4.1/7,代表10.2在第一個項目符號中)忽略任何對象,函數和枚舉器名稱(3.4.3/1--在這種情況下不存在這樣的名稱)。然後,它違反了對10.2的要求,即:

If the resulting set of declarations are not all from sub-objects of the same type ... the program is ill-formed.


這可能是VS2010和GCC解釋的C++ 0x的措辭不同於科莫和追溯執行這一措辭:

In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined.

這意味着考慮到非基類,但是如果命名了非基類,則它是錯誤的。如果標準打算忽略非基類的名字,那麼只能在這裏,或者明確地說出來(這兩個練習都完成了)。然而,該標準完全不是由於其使用可以是。而GCC實現C++ 0x的措辭,因爲它拒絕其他完全正確的C++ 03代碼,只是因爲using聲明包含它的類名。

對於措辭不清楚的一個例子,考慮以下表達式:

a.~A(); 

這在語法上是不明確的,因爲它可以是一個成員函數調用如果a是一個類對象,但它可以是一個僞 - 如果a具有標量類型(如int),則使用-destructor-call(這是不可操作的)。但是,什麼標準說是一個僞析構函數調用和類成員訪問的語法在5.2.45.2.5分別

The left-hand side of the dot operator shall be of scalar type.

For the first option (dot) the type of the first expression (the object expression) shall be 「class object」 (of a complete type).

這是錯誤的使用,因爲它不清理的模糊性可言。它應該使用「只能」,編譯器以這種方式解釋它。這主要是歷史原因,因爲一些委員會成員最近在usenet上告訴我。見附件H的The rules for the structure and drafting of International Standards

+0

@litb:我修改了類「D」,如下所示:struct D:Y,Z {using X :: f2;使用Z :: X :: f3; void f(){X * p = this;}};現在我得到了錯誤prog.cpp:19:錯誤:重複使用聲明'使用X :: f3'prog.cpp:在成員函數'void D :: f()':prog.cpp:20:error:'X '是'D'的模糊基礎 '名稱查找'期間'使用聲明'的規則是否有所不同? – Chubsdad 2010-08-23 10:22:42

+0

@chubsdad我所能做的就是重複標準。我不知道這些編譯器實現了什麼。我在代碼中看不到「重複使用聲明」。 – 2010-08-23 10:24:58

+0

@litb:我的代碼中有輕微的錯字,因此編譯器輸出。修改後的錯誤如下: prog.cpp:在成員函數'void D :: f()'中: prog.cpp:20:錯誤:'X'是'D'的模糊基地 – Chubsdad 2010-08-23 10:28:25

0

使用X :: f2;不應該工作由於私有繼承下面的代碼

struct Y : private X{ 
    void f4(); 
}; 

它是不可能通過Y. 所以X :: F2將衝突訪問X的成員。

Z::X::f2應該工作。 或Z::f2應該工作。

+0

C++具有多重訪問規則。如果一個名稱可以通過基類層次結構中的多個路徑找到,並且其中一個名稱是公共的,則會獲取該公共訪問路徑。見'11.7/1'。 – 2010-08-23 10:08:28

+0

@litb:所以這意味着'使用X :: f2'是好的,因爲'X'可以通過OP中的'Z'訪問。但是,'X'是不明確的,因此模糊相關的錯誤。因此,有關'不是所有重載都可訪問'的VS2010錯誤消息可能很棘手。這意味着Comeau是正確的。我至少懷疑有關使用「Z :: X :: f3」聲明的錯誤。 – Chubsdad 2010-08-23 10:15:58

+0

@chubsdad它只是意味着它不是一個可訪問性錯誤。它將訪問權限更改爲提供最多訪問權限的路徑。即使沒有11.7/1,它也不是一個模糊的查詢,但是我們不知道我們是否需要應用私有或公共可訪問性。 – 2010-08-23 12:12:18

0

首先,澄清Johannes的答案。當你說using Z::X::f2;時,編譯器不會「建立一條路徑」到f2以跟蹤應該如何訪問它。由於Z::XX是一樣的,所以聲明與using X::f2;完全相同。這個例子對比吧:

struct A { void f() {} void g() {} }; 
struct B { void f() {} void g() {} }; 
struct C { typedef A X; }; 
struct D { typedef B X; }; 
struct E : A, B { 
    using C::X::f; // C::X == A 
    using D::X::g; // D::X == B 
}; 

Z::X工作不繼承或會員的,而是因爲該標識符X距離範圍Z訪問的語法。你甚至可以寫出Z::Z::Z::Z::X::X::X::X令人厭惡,因爲每個班級都將自己的名字帶入自己的範圍。因此::這裏不表示繼承。

現在,解決問題。 f2YZ繼承自X。因此,它是的成員YZE不需要知道關於X,因爲它是一個隱藏的實現細節。所以,你要

struct D : Y, Z{ 
    using Y::f2; // error: inaccessible 
    using Z::f3; 
}; 

要9.1/2條款,你問解釋:

A class-name is inserted into the scope in which it is declared immediately after the class-name is seen. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name.

名稱X注入XX::X。然後它被繼承到YZYZ不會在其自己的範圍內隱式聲明X

10.2/2:

The following steps define the result of name lookup in a class scope, C. First, every declaration for the name in the class and in each of its base class sub-objects is considered. … If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed. Otherwise that set is the result of the lookup.

注意我加粗的複數詞子對象。儘管在兩個子對象中找到名稱X,但它們都是相同類型的,即X

+0

在'D'範圍內,有兩個'X'子對象(一個來自'Y'(無法訪問),另一個來自' Z'(accessible))。參考$ 9.2(Injected class name)。不應該使用X :: f2與上述邏輯模糊不清,因爲'X'是'D'的模糊基礎? – Chubsdad 2010-08-23 11:04:05

+0

@chubs:只有一個名爲'X'的類,''X'命名一個類,而不是一個子對象:這是我生病的地方ustration。 – Potatoswatter 2010-08-23 11:29:24

+0

@chubs:至於§9.1/ 2(不是$ 9.2),請參閱更新的答案。 – Potatoswatter 2010-08-23 11:37:11