我试图编译这个例子,其中可变参数类模板继承了可变量的碱基,每个碱基都实现了不同的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;
}
当我用gcc(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>]
为什么这个含糊不清?
答案 0 :(得分: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[]...;
,我们就不必这样做了。
必须注意构造函数(我没有采取),但您可能不需要这样做。
实际上出错的地方取决于我不太确定的标准细节。基本上,您是以复杂的方式混合运算符,继承和重载。即使您的代码符合标准(它可能是也可能不是),它的符合性会使某些编译器死亡。
答案 1 :(得分:2)
代码无效,gcc拒绝它是正确的(clang 3.6.0接受它 - 这是一个错误)。查找运算符的规则从[over.match.oper]开始:
[...]二进制文件 operator @,其左侧操作数的类型为cv非限定版本为T1,右操作数为类型 其cv不合格版本为T2,三组候选函数,指定成员候选人,非成员 候选人和内置候选人的构建如下:
- 如果T1是完整的类类型或当前正在定义的类,则成员候选者集合是T1::operator@
的合格查询结果(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 i )合并到中间体S(f,C)中的结果:
- [...]
- 否则,如果S(f,B i )和S(f,C)的声明集不同,则合并是不明确的:新的 S(f,C)是具有无效声明集和子对象集的并集的查找集。在随后的 合并时,认为无效的声明集与其他声明集不同 - [...]
基本上 - 我们这里有两个基类,都有一个operator[]
。两个声明集都不同 - 因此合并是不明确的。消除歧义的方法是引入 using-declaration 将所有基类成员函数引入派生类,以便初始查找集找到所有内容。
缩短你的例子:
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()