/ / nejednoznačný operátor [] v šablóne variadic - c ++, c ++ 11, preťažovanie operátora, variadic-templates, dvojznačný

nejednoznačný operátor [] vo variadic template - c ++, c ++ 11, preťaženie operátorom, variadic-templates, nejednoznačný

Snažím sa tento príklad kompilovať, keď šablóna variadickej triedy zdedí z variadického množstva báz, z ktorých každý implementuje iný 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;
}

Keď kompilujem s gcc (Debian 4.9.2-10), dostanem nasledujúcu chybu

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

Prečo je tento nejednoznačný?

odpovede:

3 pre odpoveď č. 1

Prenosný spôsob, ako robiť to, čo chcete, je zhruba:

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

potom:

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

tu lineárne zdedíme po každom z typov a using operator[] na našich rodičov.

Ak by sme mohli using Field<Fields>::operator[]...; nemuseli by sme to robiť.

Určitú starostlivosť treba venovať konštruktérom (ktoré som nebral), ale nemusíte to urobiť.

živý príklad.


Čo sa vlastne deje, závisí od detailovnormy som menej ako niektoré. V podstate ste miešanie operátorov a dedičstvo a preťaženie v komplexným spôsobom. Aj keď je váš kód štandardne kompatibilný (čo môže alebo nemusí byť), je kompatibilný tak, že niektorí kompilátori zomrú.


2 pre odpoveď č. 2

Kód je neplatný a gcc je správne ho odmietnuť (clang 3.6.0 ho akceptuje - je to chyba). Pravidlá pre vyhľadávanie operátora začínajú od [over.match.oper]:

[...] pre binárne číslo operátor @ s ľavým operandom typu, ktorého cv-nekvalifikovaná verzia je T1 a pravý operand typu ktorých cv-nekvalifikovaná verzia je T2, sú určené tri sady kandidátskych funkcií členských kandidátov, nečlen kandidáti a zabudovaní kandidáti, sú konštruované nasledovne: \ t
- Ak T1 je úplný typ triedy alebo trieda, ktorá sa práve definuje, množina kandidátov na členstvo je výsledok kvalifikovaného vyhľadávania T1::operator@ (13.3.1.1.1); inak, súbor členov kandidátov je prázdny.

Pravidlá vyhľadávania pre meno člena sú (pretože sa pozeráme hore) ctmap<last_name,age>::operator[]), z [class.member.lookup]:

Vypočíta sa vyhľadávanie nastavené pre fv C, nazvané S (f, C), [...] nasledovne:

Ak C obsahuje vyhlásenie o názve f, [...]

Inak (t. J., C neobsahuje deklaráciu f alebo výslednú množinu deklarácií je prázdna), S (f, C) je pôvodne prázdne. Ak má C základné triedy, vypočítajte množinu vyhľadávaní pre f v každom subobjektu triedy B základnej triedy. a zlúčiť každý takýto vyhľadávací súbor S (f, Bja) na S (f, C).

Nasledujúce kroky definujú výsledok zlučovacieho vyhľadávacieho súboru S (f, B.)ja) na medziprodukt S (f, C):
- [...]
- V opačnom prípade, ak súčet deklarácií S (f, B.)ja) a S (f, C) sa líšia, zlúčenie je nejednoznačné: nové S (f, C) je vyhľadávacia sada s neplatnou množinou deklarácií a spojením množín subobjektov. V nasledujúcich neplatná deklarácia sa považuje za odlišnú od všetkých ostatných.
- [...]

V podstate tu máme dve základné triedy, obe s operator[], Obe sady deklarácií sa líšia - takže zlúčenie je nejednoznačné. Spôsob, akým by sa rozpadli, by bolo predstaviť použitím vyhlásenies prináša všetky základné funkcie člena triedy do odvodenej triedy, takže počiatočný vyhľadávací súbor nájde všetko.

Ak chcete skrátiť príklad:

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

struct C : A, B { };

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

S touto hierarchiou

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

D d;
d.foo("x") // OK: calls A::foo()