4

我試圖編譯這個例子中,其中可變參數類模板從鹼基的可變參數量繼承,其中的每一個實現了不同operator[]曖昧操作符[]中可變參數模板

#include <iostream> 

template <typename T> 
struct Field { 
    typename T::value_type storage; 

    typename T::value_type &operator[](const T &c) { 
    return storage; 
    } 
}; 

template<typename... Fields> 
struct ctmap : public Field<Fields>... { 
}; 

int main() { 
    struct age { typedef int value_type; }; 
    struct last_name { typedef std::string value_type; }; 

    ctmap<last_name, age> person; 

    person[last_name()] = "Smith"; 
    person[age()] = 104; 
    std::cout << "Hello World!" << std::endl; 
    return 0; 
} 

當我編譯與海灣合作委員會(Debian的4.9.2-10),我得到以下錯誤

main.cpp: In function ‘int main()’: 
main.cpp:22:23: error: request for member ‘operator[]’ is ambiguous 
    person[last_name()] = "Smith"; 
        ^
main.cpp:7:27: note: candidates are: typename T::value_type& Field<T>::operator[](const T&) [with T = main()::age; typename T::value_type = int] 
    typename T::value_type &operator[](const T &c) { 
         ^
main.cpp:7:27: note:     typename T::value_type& Field<T>::operator[](const T&) [with T = main()::last_name; typename T::value_type = std::basic_string<char>] 
main.cpp:23:17: error: request for member ‘operator[]’ is ambiguous 
    person[age()] = 104; 
       ^
main.cpp:7:27: note: candidates are: typename T::value_type& Field<T>::operator[](const T&) [with T = main()::age; typename T::value_type = int] 
    typename T::value_type &operator[](const T &c) { 
         ^
main.cpp:7:27: note:     typename T::value_type& Field<T>::operator[](const T&) [with T = main()::last_name; typename T::value_type = std::basic_string<char>] 

爲什麼這種含糊?

+1

這是由鐺接受++ 3.8:http://melpon.org/wandbox/permlink/huzwGp0kc2OafMZl(但我在這種情況下,我不確定哪個編譯器是正確的) – dyp

+0

(基本上,多個基類中的成員函數不會超載,但我不確定它如何與運算符查找相互作用;乍一看似乎是錯誤的) – dyp

+0

你可以使它符合線性/ t基於ree的繼承和'使用'聲明。 – Yakk

回答

3

可移植的方式做你想要的東西大致是:

template<class...Ts> 
struct operator_index_inherit {}; 
template<class T0, class T1, class...Ts> 
struct operator_index_inherit<T0, T1, Ts...>: 
    T0, operator_index_inherit<T1, Ts...> 
{ 
    using T0::operator[]; 
    using operator_index_inherit<T1, Ts...>::operator[]; 
}; 
template<class T0> 
struct operator_index_inherit<T0>: 
    T0 
{ 
    using T0::operator[]; 
}; 

則:

template<class... Fields> 
struct ctmap : operator_index_inherit<Field<Fields>...> { 
    using base = operator_index_inherit<Field<Fields>...>; 
    using base::operator[]; 
}; 

這裏我們從線性每種類型的繼承和using operator[]對我們的父母。

如果我們可以using Field<Fields>::operator[]...;我們不必這樣做。

一些關心必須與構造函數(我沒有采取),但你可能不需要這樣做。

live example


什麼是實際發生錯誤取決於標準的細節我是少於某些。基本上,你是以複雜的方式混合運算符和繼承和超載。即使您的代碼符合標準(可能也可能不符合標準),它仍然符合某些編譯器的要求。

+1

我不明白這是如何回答tbh –

+0

WOW的問題...它的工作,可能需要我花一段時間才能完全理解您的示例(我對C++ 11還不完全熟練),非常感謝您 – Trungus

+0

@light是真的,但它確實解決了這個問題。 – Yakk

2

代碼無效,gcc拒絕它是正確的(雖然叮噹聲3.6.0接受 - 這是一個錯誤)。用於查找操作的規則入手,從[over.match.oper]:

[...]與類型,其CV-不合格的左操作數的二進制 操作@版本是T1和類型 ,其CV-不合格版本是T2,三組候選的功能,指定構件候選非成員 候選內置候選,被構造爲的右操作數如下:
- 如果T1是完整的類別類型或當前正在定義的類別,候選成員集合是 T1::[email protected](13.3.1.1.1)的合格查詢結果;否則,候選人集合 爲空。

的成員名稱查找規則(因爲我們仰視ctmap<last_name,age>::operator[]),從[class.member.lookup]:

在C f顯示查找集,稱爲S( F,C),[...]被計算 如下:

如果C含有名稱f的聲明,[...]

否則(即,C不包含f的聲明或者結果聲明集爲空),S(f,C)爲 最初爲空。如果C具有基類,則計算每個直接基類子對象Bi, 中的f的查找集,並將每個這樣的查找集合S(f,B i)依次合併爲S(f,C)。

以下步驟定義合併查找集合S的結果(F,B )到中間S(F,C):
- [...]
- 否則,如果聲明集S(f,B i)和S(f,C)不同,合併是不明確的:新的 S(f,C)是一個帶有無效聲明集和子對象集合的查找集。在隨後的 合併中,無效聲明集被認爲與其他任何聲明集都不相同。
- [...]

基本上 - 我們這裏有兩個基類,都與一個operator[]。兩個聲明集合都不相同 - 所以合併是不明確的。消除歧義的方法是引入使用聲明將所有基類成員函數引入派生類,以便初始查找集找到所有內容。

爲了縮短你的例子:

struct A { void foo(char) { } }; 
struct B { void foo(int) { } }; 

struct C : A, B { }; 

struct D : A, B { 
    using A::foo; 
    using B::foo; 
}; 

隨着該層次

C c; 
c.foo(4); // error: ambiguous lookup set for foo() 

D d; 
d.foo('x') // OK: calls A::foo() 
+0

我明白....我有一個問題,我怎麼能調用B :: foo()? ...我嘗試投射「4」,或者聲明一個變量int,但總是給出模糊的查找。 – Trungus

+0

@Trungus參數無關緊要 - 這是名稱「foo」的實際查找失敗,因爲它不明確。要調用它,你需要有* using-declaration *('在類中使用B :: foo')。或者你可以通過指向成員的指針來調用它('(C {}。*&B :: foo)(4);'<==不寫這段代碼,我只是爲了完整而包含它) – Barry

+0

correct me如果我錯了,但是你的例子在類聲明中已經有了「using B:foo」,爲什麼仍然有模糊的查找錯誤? – Trungus