2012-05-20 11 views
18

Zauważyłem, że po uzyskaniu dostępu do atrybutu innego niż szablon (v.foo) ze zmiennej typu szablonu (T& v), C++ można nabrać na myślenie, że jest to szablon elementu, jeśli istnieje jest funkcją szablonu o tej samej nazwie (template class <T> void foo()). Jak to wyjaśnić w specyfikacji C++? Rozważmy ten prosty program:C++ mylące nazwa atrybutu dla szablonu członkowskiego

#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ę szablonu foo_negative że trwa obiekt dowolnego typu i określa, czy jego atrybut foo jest ujemna. Funkcja main tworzy instancję foo_negative z [T = X]. Ten program kompiluje i działa bez żadnych danych wyjściowych.

Teraz dodać tę funkcję do górnej części programu:

template <class T> 
void foo() 
{ 
} 

kompilowanie go z G ++ 4.6.3 powoduje ten 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 Linia 13 jest return v.foo < 0 i Linia 25 to assert(!foo_negative(x)).)

Klang powoduje podobne błędy.

Wat? W jaki sposób dodanie niepowiązanej funkcji, która nigdy nie jest nazywana, powoduje wprowadzenie błędu składni do poprawnego programu? Podczas analizowania foo_negative kompilator nie zna typu v i, co najważniejsze, nie wie, czy v.foo jest szablonem członka, czy zwykłym członkiem. Najwyraźniej musi zdecydować w czasie analizowania (zanim szablon zostanie utworzony), czy traktować go jako szablon członkowski czy zwykłego członka.

Jeśli uzna v.foo szablon jest członkiem, a następnie < 0 jest postrzegany jako uboczny 0 jako szablonu argumentu, i jest tam brakuje >, stąd błąd składni. Następnie, gdy foo_negative jest tworzone z [T = X], pojawia się kolejny błąd, ponieważ X::foo nie jest szablonem członkowskim.

Ale dlaczego uważasz, że v.foo jest szablonem członka? Ta niejednoznaczność jest właśnie tym słowem kluczowym template: jeśli napisałem v.template foo, to wyraźnie powiedziałbym C++, aby oczekiwał szablonu członkowskiego, ale nie użyłem słowa kluczowego template! Nie odwoływałem się do szablonu członkowskiego, więc należy założyć, że jest to zwykły członek. Fakt, że istnieje funkcja 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.

+0

To dziwne. Kompiluje on na gcc-4.3.4: http://ideone.com/FP3SO –

+0

Nie, zapomniałeś dodać 'template void foo()' declaration: http://ideone.com/FxEBm Nie kompiluje . – mgiuca

+4

być może jest to błąd zarówno w gcc jak i clang: [declaration] (http://ideone.com/zofCF). Zmieniłem 'v.foo < 0' to '0 > v.foo' i kompiluje się dobrze. Być może oba kompilatory uważają, że '<0' jest argumentem szablonu, o czym wspomniałeś w pytaniu. – user2k5

Odpowiedz

2

W języku Clang było to PR11856, które zostało naprawione ~ 2,5 miesiąca temu. Clang trunk i Clang 3.1 nie zgłaszają żadnych błędów z this code. Clang bug zawiera explanation dlaczego ten kod został odrzucane i dlaczego kod jest poprawny, reprodukowana tutaj (lekko manipulowane, aby rozwiązać sprawę):

The paragraph that matters here is [basic.lookup.classref]p1:

"In a class member access expression (5.2.5), if the . or -> token is immediately followed by an identifier followed by a < , the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template."

Since v is dependent, presumably the identifier is not found so we consider what happens if we look in the context of the entire postfix-expression. Since we find a function template, we should not conclude that we have the start of a template-id.

+0

Dzięki. Właśnie znalazłem [odpowiedni raport o błędzie w GCC] (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10200), który został zgłoszony w 2003 roku, jeszcze nie naprawiony. – mgiuca

4

To jest błąd.

Twój kod działa poprawnie na MSVC (2011). Myślę, że analizator składni przetłumaczył "<" jako znacznik początkowy dla instrukcji szablonu. Ale dlaczego Clang i GCC mają ten błąd na sam czas?

Proszę zgłosić błąd here i here.

Może to również interesujący: Another bug in g++/Clang? [C++ Templates are fun]

+0

Czy na pewno jest to błąd w GCC/Clang, a nie w MSVC? Jeśli chcesz pokazać, że to błąd, musisz pokazać go ze specyfikacji, a nie z innej implementacji. Szczególnie Microsoft - mają historię zezwalającą na kompilację rzeczy, których nie powinno się ściśle stosować. Przejrzałem specyfikację, ale jest to dość trudne do zrozumienia (przeczytam trochę później) - obstawiam, że jest bardziej prawdopodobne, że w specyfikacji znajduje się pewna klauzula (prawdopodobnie §14.2.3), że G ++ i Clang podąża za literą, a MSVC ignoruje, ale może być odwrotnie. – mgiuca

+0

I na pewno zagram błąd, jeśli można pokazać, że naruszają specyfikację. Po prostu z mojego doświadczenia wynika, że ​​jeśli w kompilatorze jest błąd, zazwyczaj jest to a) Twój kod, lub b) nie czytasz specyfikacji wystarczająco dokładnie. – mgiuca

+0

może ... ale specyfikacja C++ jest ogromna i jest bardzo skomplikowana. Wiem, że MSVC nie jest najlepszym kompilatorem, który jest zgodny ze specyfikacją C++, ale myślę, że najlepszą rzeczą do zrobienia jest teraz zgłoszenie błędu ... może to jest albo to nie jest błąd ... – pearcoding

6

To wygląda jak błąd kompilatora.

Norma mówi:

After name lookup (3.4) finds that a name is a template-name or that an operator-function-id or a literal-operator-id refers to a set of overloaded functions any member of which is a function template if this is followed by a <, the < is always taken as the delimiter of a template-argument-list and never as the less-than operator.

aw 3.4.5/1

In a class member access expression (5.2.5), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template.

Norma nie wydają się wskazywać, że nazwa odnośnika może kiedykolwiek znaleźć szablon funkcji non-member tutaj. W każdym razie znaczenie terminu < powinno być ustalone w czasie definiowania szablonu, a nie w czasie tworzenia instancji (jest za późno).