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 typuvi, 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.

questionAnswers(3)

yourAnswerToTheQuestion