I'm trying to compile this example, where a variadic class template inherits from a variadic amount of bases, each of which implements a different 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;
}
When I compile with gcc (Debian 4.9.2-10), I get the following error
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>]
Why is this ambiguous?
The code is invalid and gcc is correct to reject it (clang 3.6.0 accepts it though - this is a bug). The rules for looking up an operator start with, from [over.match.oper]:
The lookup rules for a member name are (since we're looking up
ctmap<last_name,age>::operator[]
), from [class.member.lookup]:Basically - we have two base classes here, both with an
operator[]
. Both declaration sets differ - so the merge is ambiguous. The way to disambiguate would be to introduce using-declarations bringing in all the base class member functions into the derived class, so that the initial lookup set finds everything.To shorten your example:
With that hierarchy
A portable way do do what you want is roughly:
then:
here we linearly inherit from each of the types, and
using operator[]
on our parents.If we could
using Field<Fields>::operator[]...;
we would not have to do this.Some care has to be taken with constructors (which I did not take), but you might not need to do this.
live example.
What is actually going wrong depends on details of the standard I am less than certain of. Basically, you are mixing operators and inheritance and overloading in a complex way. Even if your code is standard compliant (which it may or may not be), it is compliant in a way that some compilers die on.