2013-03-17 13 views
6

Lubię używać klas lokalnych w klasach szablonów do wykonywania konstrukcji takich jak "statyczne, jeśli". Ale stanąłem przed problemem, że gcc 4.8 nie chce skompilować mojego kodu. Jednak 4.7 to.C++ GCC Dlaczego ten kod sfinae może być skompilowany z GCC 4.7, ale nie z 4.8?

Próbka:

#include <type_traits> 
#include <iostream> 
#include <string> 

using namespace std; 

struct A { 
    void printA() { 
     cout << "I am A" << endl; 
    } 
}; 
struct B { 
    void printB() { 
     cout << "I am B" << endl; 
    } 
}; 

template <typename T> 
struct Test { 
    void print() { 
     struct IfA { 
      constexpr IfA(T &value) : value(value) { 
      } 
      T &value; 
      void print() { 
       value.printA(); 
      } 
     }; 
     struct IfB { 
      constexpr IfB(T &value) : value(value) { 
      } 
      T &value; 
      void print() { 
       value.printB(); 
      } 
     }; 
     struct Else { 
      constexpr Else(...) {} 
      void print() { 
      } 
     }; 
     typename conditional<is_same<T, A>::value, IfA, Else>::type(value).print(); 
     typename conditional<is_same<T, B>::value, IfB, Else>::type(value).print(); 
    } 
    T value; 
}; 

int main() { 
    Test<A>().print(); 
    Test<B>().print(); 
} 

Opcje:

g++ --std=c++11 main.cc -o local-sfinae 

Zadanie:

  1. Biorąc Klasy A i B z różnymi interfejsami do druku.
  2. Napisz ogólny test, który może drukować zarówno A, jak i B.
  3. Nie zanieczyszczaj żadnej przestrzeni nazw ani zakresu klasy.

Opis kodu:

  1. To tylko czysty przykład.
  2. Używam takiego podejścia, ponieważ chcę generalizować konstrukcję "static if". Zobacz, że przekazuję argumenty do klas IfA i IfB za pośrednictwem ich pól, a nie bezpośrednio do funkcji print().
  3. Bardzo często stosuję takie konstrukcje.
  4. Zauważyłem, że te konstrukcje nie powinny znajdować się w zasięgu (zanieczyszczającym) klasy. Mam na myśli, że powinny one być umieszczone w zakresie metody.

Więc pytanie.

Tego kodu nie można skompilować za pomocą GCC 4.8. Ponieważ sprawdza WSZYSTKIE klasy, nawet jeśli nigdy nie są używane. Ale nie tworzy ich w systemie binarnym (skomentowałem linie, które powodują błędy i skompilowałem je za pomocą gcc 4.8). Dowód:

$ nm local-sfinae |c++filt |grep "::If.*print" 
0000000000400724 W Test<A>::print()::IfA::print() 
00000000004007fe W Test<B>::print()::IfB::print() 

Zobacz, nie ma testu :: print() :: IfB :: print(). (Patrz dalej: 'void test :: print() :: IFB :: print() [z T = A]')

błędów jeśli skompilować wspomniany kod z gcc 4.8:

g++ --std=c++11 main.cc -o local-sfinae 
main.cc: In instantiation of 'void Test<T>::print()::IfB::print() [with T = A]': 
main.cc:36:9: required from 'void Test<T>::print() [with T = A]' 
main.cc:49:21: required from here 
main.cc:34:17: error: 'struct A' has no member named 'printB' 
       value.printB(); 
       ^
main.cc: In instantiation of 'void Test<T>::print()::IfA::print() [with T = B]': 
main.cc:28:9: required from 'void Test<T>::print() [with T = B]' 
main.cc:50:21: required from here 
main.cc:26:17: error: 'struct B' has no member named 'printA' 
       value.printA(); 
       ^
  1. Czy jest to błąd GCC 4.8?
  2. Czy to błąd GCC 4.7? Może kod nie powinien być kompilowany.
  3. Albo to jest mój błąd, a ja nie powinienem polegać na zachowaniu kompilatora/nie powinienem stosować takiego podejścia do implementacji "static if".

Dodatkowe informacje:

Ten prosty kod kompiluje się na 4.7, ale nie na 4,8. Skróciłem to.

struct A { 
    void exist() { 
    } 
}; 

template <typename T> 
struct Test { 
    void print() { 
     struct LocalClass { 
      constexpr LocalClass(T &value) : value(value) { 
      } 
      T &value; 
      void print() { 
       value.notExist(); 
      } 
     }; 
    } 
    T value; 
}; 

int main() { 
    Test<A>().print(); 
} 

Błędy:

main.cc: In instantiation of 'void Test<T>::print()::LocalClass::print() [with T = A]': 
main.cc:16:9: required from 'void Test<T>::print() [with T = A]' 
main.cc:22:21: required from here 
main.cc:14:17: error: 'struct A' has no member named 'notExist' 
       value.notExist(); 
       ^

testowałem dwie wersje GCC 4.8: 2012.10 i 2013.02. Mam nadzieję, że jest to błąd GCC 4.8 i można go naprawić.

+0

źle sformułowany kod o tym, jaki standard mówi, że "brak diagnostyki wymaganej" jest nadal nieważny. Kompilator może więc odmówić skompilowania go. –

Odpowiedz

3

LocalClass nie jest szablonem. Reguła "nie tworzy instancji, jeśli nie jest używana" ma zastosowanie tylko do funkcji składowych szablonów klas.

Oznacza to, że po utworzeniu instancji Test::print() wszystko, co jest w środku, zostaje przywrócone do życia, w tym nieużywanego członka lokalnej klasy.

3

W Twoim kodzie nie ma SFINAE.

SFINAE zastosowanie podczas odliczenia argumentów szablonu i substytucji argumentu (zwanego dalej „S” w SFINAE oznacza podstawienie), ale tylko podstawienie w programie dzieje, gdy zastępując A dla T na liście parametrów szablonu Test, który nie zawodzi .

Następnie należy zadzwonić pod numer print(), który tworzy instancję Test<A>::print(), która nie zawiera żadnych podstawień, i pojawia się błąd, ponieważ value.notExist(); jest niepoprawny.

SFINAE musi być używany w kontekstach zastępczych, takich jak odliczenia szablon argumentu spowodowane przez wywołanie funkcji lub gdy wnioskowanie parametry szablonu z domyślnych argumentów.

+0

Mam na myśli, że zawiera dużo kodu sfinae. Wydaje się jednak, że ani GCC 4.7 ani 4.8 naprawdę nie tworzą instancji LocalClass. Ale GCC 4.8 również zwraca błąd. Dziękuję za Twój komentarz. Zrobiłem lepszy mechanizm przełączania małych typów: http://ideone.com/v1KmTA – cppist

+0

As @ n.m. w drugiej odpowiedzi "LocalClass" nie jest szablonem, więc nie można uzyskać instancji. Jego definicja jest nieprawidłowa, co zostaje zdiagnozowane, gdy zostanie utworzony otaczający szablon funkcji –

Powiązane problemy