2017-09-30 27 views
13

Poniższy kod:dostać <string> dla odmiany nie pod brzękiem ++ ale nie g ++

variant<string> x = "abc"; 
cout << get<string>(x) << "\n"; 

działa dobrze pod g ++ (wersja 7.2). Jednak, gdy skompilowany pod brzękiem ++ (wersja 5.0), używając libstdC++, pojawia się następujący błąd w metodzie get:

/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/7.2.0/../../../../include/c++/7.2.0/variant:238:46: fatal error: cannot cast 'std::variant<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >' to its private base class 'std::__detail::__variant::_Variant_storage<false, std:: 
__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >' 
     return __get(std::in_place_index<_Np>, std::forward<_Variant>(__v)._M_u); 

Jest to błąd kompilatora, czy mój kod nielegalne w jakikolwiek sposób?

+0

Czy określiłeś, że 'libC++' powinno być używane jako standardowa implementacja biblioteki? IIRC, clang używa domyślnie libstdC++. – StoryTeller

+0

Nie widzę, co tutaj jest nielegalne, ale jeśli tak, to byłoby całkiem podstawową rzeczą zepsuć ... –

+2

Biorąc pod uwagę, że działa w gcc + libstdC++, działa w clang + libC++, ale kończy się niepowodzeniem w clang + libstdC++, najprawdopodobniej jest to subtelna niezgodność między clang a libstdC++. Odpowiedź powinna wskazywać dokładnie, czym jest niezgodność. – hvd

Odpowiedz

13

Jest to spowodowane clang bug 31852 (a także 33222), którego reprodukcja uprzejmości Jonathana Wakely powinien wyglądać bardzo istotne:

template<typename V> auto get(V&) { } 

template<typename> 
class variant 
{ 
    template<typename V> friend auto get(V&); 
}; 

int main() 
{ 
    variant<int> v{}; 
    get(v); // error: ambiguous 
} 

dzyń nie rozpoznaje przyjaciół deklaracji, które mają typy zastępcze. Który jest dokładnie jak libstdC++ implementuje std::get:

// Returns the typed storage for __v. 
template<size_t _Np, typename _Variant> 
constexpr decltype(auto) __get(_Variant&& __v) 
{ 
    return __get(std::in_place_index<_Np>, std::forward<_Variant>(__v)._M_u); 
} 

ten uzyskuje dostęp do prywatnego członek variant, ale ta funkcja jest properly declared a friend:

template<size_t _Np, typename _Vp> 
friend constexpr decltype(auto) __detail::__variant::__get(_Vp&& __v); 

libstdC++ 's realizacja jest ważna, dzyń po prostu nie myśleć __get jest friend.

+2

Jakieś znane rozwiązania? – papirrin

+1

Myślę, że najlepszym i rozsądnie bezpiecznym rozwiązaniem jest użycie zmodyfikowanej wersji pliku nagłówkowego libstdC++ podczas programowania. Wszystko, co musisz zmienić, to komentowanie deklaracji znajomego i publiczne wyprowadzenie z __detail :: _ variant :: _ Variant_base (która daje __get dostęp do bazowego wariantu pamięci). Używam tego do wyciszania ton błędów z clang-tidy podczas używania GCC jako podstawowego kompilatora i nie zauważyłem żadnych złych skutków ubocznych. – Stacker