可变参数模板中的模糊运算符[]
ambiguous operator[] in variadic template
我正在尝试编译此示例,其中可变 class 模板继承自可变数量的基,每个基实现不同的 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>]
为什么这么模棱两可?
做你想做的便携方式大致是:
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[]
在我们的 parents.
如果可以using Field<Fields>::operator[]...;
我们就不必这样做了。
必须注意构造函数(我没有注意),但您可能不需要这样做。
实际出了什么问题取决于我不太确定的标准细节。基本上,您正在以一种复杂的方式混合运算符、继承和重载。即使您的代码符合标准(可能符合也可能不符合),但它的符合性会导致一些编译器死掉。
代码无效,gcc 拒绝它是正确的(虽然 clang 3.6.0 接受它 - 这是一个错误)。查找运算符的规则从 [over.match.oper]:
开始
[...] for a binary
operator @ with a left operand of a type whose cv-unqualified version is T1 and a right operand of a type
whose cv-unqualified version is T2, three sets of candidate functions, designated member candidates, non-member
candidates and built-in candidates, are constructed as follows:
— If T1 is a complete class type or a class currently being defined, the set of member candidates is the
result of the qualified lookup of T1::operator@
(13.3.1.1.1); otherwise, the set of member candidates
is empty.
成员名称的查找规则是(因为我们正在查找 ctmap<last_name,age>::operator[]
),来自 [class.member.lookup]:
The lookup set for f in C, called S(f,C), [...] is calculated
as follows:
If C contains a declaration of the name f, [...]
Otherwise (i.e., C does not contain a declaration of f or the resulting declaration set is empty), S(f,C) is
initially empty. If C has base classes, calculate the lookup set for f in each direct base class subobject Bi,
and merge each such lookup set S(f,Bi) in turn into S(f,C).
The following steps define the result of merging lookup set S(f,Bi) into the intermediate S(f,C):
— [...]
— Otherwise, if the declaration sets of S(f,Bi) and S(f,C) differ, the merge is ambiguous: the new
S(f,C) is a lookup set with an invalid declaration set and the union of the subobject sets. In subsequent
merges, an invalid declaration set is considered different from any other.
— [...]
基本上 - 我们这里有两个基数 class,都有一个 operator[]
。两个声明集不同 - 所以合并是不明确的。消除歧义的方法是引入 using-declarations 将所有基本 class 成员函数引入派生 class,以便初始查找集找到一切。
要缩短您的示例:
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()
我正在尝试编译此示例,其中可变 class 模板继承自可变数量的基,每个基实现不同的 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>]
为什么这么模棱两可?
做你想做的便携方式大致是:
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[]
在我们的 parents.
如果可以using Field<Fields>::operator[]...;
我们就不必这样做了。
必须注意构造函数(我没有注意),但您可能不需要这样做。
实际出了什么问题取决于我不太确定的标准细节。基本上,您正在以一种复杂的方式混合运算符、继承和重载。即使您的代码符合标准(可能符合也可能不符合),但它的符合性会导致一些编译器死掉。
代码无效,gcc 拒绝它是正确的(虽然 clang 3.6.0 接受它 - 这是一个错误)。查找运算符的规则从 [over.match.oper]:
开始[...] for a binary operator @ with a left operand of a type whose cv-unqualified version is T1 and a right operand of a type whose cv-unqualified version is T2, three sets of candidate functions, designated member candidates, non-member candidates and built-in candidates, are constructed as follows:
— If T1 is a complete class type or a class currently being defined, the set of member candidates is the result of the qualified lookup ofT1::operator@
(13.3.1.1.1); otherwise, the set of member candidates is empty.
成员名称的查找规则是(因为我们正在查找 ctmap<last_name,age>::operator[]
),来自 [class.member.lookup]:
The lookup set for f in C, called S(f,C), [...] is calculated as follows:
If C contains a declaration of the name f, [...]
Otherwise (i.e., C does not contain a declaration of f or the resulting declaration set is empty), S(f,C) is initially empty. If C has base classes, calculate the lookup set for f in each direct base class subobject Bi, and merge each such lookup set S(f,Bi) in turn into S(f,C).
The following steps define the result of merging lookup set S(f,Bi) into the intermediate S(f,C):
— [...]
— Otherwise, if the declaration sets of S(f,Bi) and S(f,C) differ, the merge is ambiguous: the new S(f,C) is a lookup set with an invalid declaration set and the union of the subobject sets. In subsequent merges, an invalid declaration set is considered different from any other.
— [...]
基本上 - 我们这里有两个基数 class,都有一个 operator[]
。两个声明集不同 - 所以合并是不明确的。消除歧义的方法是引入 using-declarations 将所有基本 class 成员函数引入派生 class,以便初始查找集找到一切。
要缩短您的示例:
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()