2012-01-29 14 views
12

Dzisiaj myślałem, że będzie to miły pomysł, aby przeładowywać operator<< dla tablic typu C:przeciążanie operatora << dla tablic

template<typename T, size_t N> 
std::ostream& operator<<(std::ostream& os, T(&a)[N]) 
{ 
    os << '{' << a[0]; 
    for (size_t i = 1; i < N; ++i) 
    { 
     os << ',' << ' ' << a[i]; 
    } 
    os << '}'; 
    return os; 
} 

int main() 
{ 
    int numbers[] = {2, 3, 5, 7, 11, 13, 17, 19}; 
    std::cout << numbers << '\n'; 
} 

Rzeczywiście, to drukuje {2, 3, 5, 7, 11, 13, 17, 19} ładnie. Jednak dzięki temu przeciążeniu nie mogę już drukować literałów ciągów znaków:

std::cout << "hello world\n"; 

error: ambiguous overload for 'operator<<' in 'std::cout << "hello world\012"' 
note: candidates are: 

note: std::basic_ostream<_CharT, _Traits>::__ostream_type& 
std::basic_ostream<_CharT, _Traits>::operator<<(long int) [with _CharT = char, _ 
Traits = std::char_traits<char>, std::basic_ostream<_CharT, _Traits>::__ostream_ 
type = std::basic_ostream<char>] <near match> 

note: no known conversion for argument 1 from 'const char [13]' to 'long int' 

To jest naprawdę zagadkowe. Dlaczego kompilator bierze pod uwagę przeciążenie long int, gdy nie ma konwersji z const char[13] na long int w pierwszej kolejności?

Wariacje tego komunikatu o błędzie pojawia się na long unsigned int, short int, short unsigned int, int, unsigned int, long long int i long long unsigned int.

(Inni kandydaci są const void*, const char* i const _CharT*, a mój własny szablon.)


I rozwiązać problem poprzez dostarczanie szablon tylko typów zakaz Znak:

template<typename T, size_t N> 
typename std::enable_if< 
    !std::is_same<typename std::remove_cv<T>::type, char>::value, 
std::ostream&>::type operator<<(std::ostream& os, T(&a)[N]) 

Ale Nadal jestem zaskoczony pytaniem, dlaczego kompilator uznał typy numeryczne za kandydatów.

Odpowiedz

3

Pierwszym etapem rozwiązywania przeciążenia jest identyfikacja funkcjonalnych funkcji, które są w stanie zaakceptować liczbę dostarczonych argumentów (całkowicie ignorując typy). (Patrz np. 13.3.2 [over.match.viable]).

Następnie rozważa się wszystkie potrzebne konwersje w celu określenia, która z nich jest jedyną najlepszą możliwą do zrealizowania funkcją.

W tym przypadku nie ma takiego wyjątkowego najlepszego (jest dwóch równie dobrych kandydatów).

Komunikat o błędzie może właśnie informować o dwóch niejednoznacznych przypadkach. Ale myślę, że próbują być pomocni, pokazując, dlaczego stracili wszystkie pozostałe opłacalne funkcje. Czasami jest to przydatne, gdy nie możesz się domyślić, dlaczego funkcja, którą chcesz wywołać, nie została uwzględniona.

Ale zgadzam się, że głównie jest to po prostu dużo hałasu, szczególnie dla funkcji takich jak operator << lub operator >> (lub nawet operator []), które mają wiele przeciążeń.

1

Kompilator jest właściwy do odrzucenia programu. Myślę, że kluczem jest to, że twoje przeciążenie i ostream::operator<<(char const *) pojawiają się w komunikacie o błędzie. Te integralne są prawdopodobnie czerwonym śledziem ... możesz reinterpret_cast wskazówką (lub literałem ciągłym) na long int (§5.2.10/4), ale to z pewnością nie jest standardowa konwersja. Być może kompilator stara się być pomocny, dając więcej przeciążeń.

Biorąc pod uwagę przeciążenie i podzespół ostream, rozdzielczość przeciążania zawodzi, ponieważ nie istnieje żadna zasada pierwszeństwa, aby zdecydować między nimi (13.3.1.2). Tak więc, ponieważ przeciążenie członka char const * jest jedynym, z którym można się uporać, poprawka wydaje się odpowiednia.

+0

'std :: ostream' jest odniesieniem do klasy. – Mankarse

Powiązane problemy