Ambiguous operator [] in a variational pattern

I am trying to compile this example, where the template of a variational class is inherited from the variational number of bases, each of which implements a different one 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?

+4
source share
2 answers

A portable way to do what you need is approximately:

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[];
};

then

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

here we linearly inherit from each of the types, and using operator[]- from our parents.

If we could using Field<Fields>::operator[]...;, we would not have to do it.

Some attention should be paid to designers (which I did not accept), but you may not need to do this.

.


, , . , . , , . ( ), , .

+3

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]:

f C, S (f, C), [...] :

C f, [...]

(.. C f ), S (f, C) . C , f Bi, 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[]. , . , -, - , .

:

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()
+2

Source: https://habr.com/ru/post/1598758/


All Articles