C ++ myląca nazwa atrybutu dla szablonu członka
Odkryłem, że podczas uzyskiwania dostępu do atrybutu innego niż szablon (v.foo
) ze zmiennej typu szablonu (T& v
), C ++ może zostać oszukany do myślenia, że jest szablonem członkowskim, jeśli istnieje funkcja szablonu o tej samej nazwie (template class <T> void foo()
). Jak można to wyjaśnić ze specyfikacji C ++? Rozważ ten prosty program:
#include <cassert>
/** Determine whether the 'foo' attribute of an object is negative. */
template <class T>
bool foo_negative(T& v)
{
return v.foo < 0;
}
struct X
{
int foo;
};
int main()
{
X x;
x.foo = 5;
assert(!foo_negative(x));
return 0;
}
Mamy funkcję szablonufoo_negative
pobiera obiekt dowolnego typu i określa, czy jego atrybut foo jest ujemny. Themain
instancje funkcjifoo_negative
z [T = X]. Ten program kompiluje się i działa bez żadnego wyjścia.
Teraz dodaj tę funkcję do początku programu:
template <class T>
void foo()
{
}
Kompilowanie go za pomocą G ++ 4.6.3 powoduje błąd kompilatora:
funcs.cpp: In function ‘bool foo_negative(T&)’:
funcs.cpp:13:14: error: parse error in template argument list
funcs.cpp: In function ‘bool foo_negative(T&) [with T = X]’:
funcs.cpp:25:5: instantiated from here
funcs.cpp:13:14: error: ‘foo’ is not a member template function
(Gdzie jest linia 13return v.foo < 0
a linia 25 toassert(!foo_negative(x))
.)
Clang generuje podobne błędy.
Wat? W jaki sposób dodanie niepowiązanej funkcji, która nigdy nie jest wywoływana, pozwala wprowadzić błąd składniowy do poprawnego programu? Podczas analizowaniafoo_negative
, kompilator nie zna typuv
i, co najważniejsze, nie wie, czyv.foo
jest szablonem członka lub zwykłym członkiem. Najwyraźniej w czasie analizowania (przed utworzeniem szablonu) musi zdecydować, czy traktować go jako szablon członka czy zwykłego członka.
Jeśli to myśliv.foo
to jest szablon członka< 0
jest postrzegany jako przemijający0
jako argument szablonu i brakuje>
, stąd błąd składniowy. Wtedy, kiedyfoo_negative
jest tworzony z [T = X], jest inny błąd, ponieważX::foo
nie jest szablonem członkowskim.
Ale dlaczego to myśliv.foo
to jest szablon członka? Ta dwuznaczność jest dokładnie tym, czymtemplate
słowo kluczowe jest dla: jeśli napisałemv.template foo
, wtedy wyraźnie powiedziałbym C ++, aby oczekiwał szablonu szablonu, ale nie użyłemtemplate
słowo kluczowe! Nie odwołałem się do szablonu członka, więc powinien zakładać, że jest to zwykły członek. Fakt, że jestfunkcjonować o tej samej nazwie co członek nie powinien mieć żadnego efektu. Dlaczego to robi Nie może to być błąd w kompilatorze, ponieważ GCC i clang są spójne.