W następnym elementach kodu (Live on Coliru):

#include <iostream>
#include <string>

int main()
{
    struct S {
        operator bool        () const { return false; }
        operator std::string () const { return "false"; }
    } s;
    std::cout << s << "\n"; // outputs 0
}

Jak wybierz kompilator , aby wybrać wewnętrznej konwersji do bool nad std::string?

Moja hipoteza jest taka, że w tym przypadku może być wyłącznie kolejność deklaracji różnych smaków {X0}}, ale czy to wszystko? Czy standard mówi coś o wybraniu konkretnej konwersji niejawnej?

12
YSC 15 luty 2017, 17:25

2 odpowiedzi

Najlepsza odpowiedź

Przypomnijmy, że std::string nie jest typem samodzielnego, to naprawdę specjalizacja szablonu klasy - std::basic_string<char>. Bardzo ważnym szczegółami jest to, że potencjalny przeciążenie strumieniowania std::string nie bierze argumentu std::string const&, jest to Szablon funkcji, który poświęca std::basic_string const&:

template <class CharT, class Traits, class Allocator>
std::basic_ostream<CharT, Traits>& 
    operator<<(std::basic_ostream<CharT, Traits>& os, 
               const std::basic_string<CharT, Traits, Allocator>& str);

Odliczenie szablonów nigdy nie uważa konwersji . Wyszukaj nazwę znajdzie ten szablon funkcji, a następnie wyrzucić jako nieodparobalne z powodu awarii odliczenia. S nie jest basic_string<CharT, Traits, Allocator> dla żadnych takich typów, więc skończyliśmy. Jedynymi rentownymi operatorami strumienia byliby wszystkimi integralnymi, o których bool jest najlepszym meczem.

Jeśli istnieje konkretnie funkcja z podpisem:

std::ostream& operator<<(std::ostream&, std::string const& );    

Następnie połączenie byłoby niejednoznaczne - otrzymasz dwa konwersje zdefiniowane przez użytkownika, które byłyby równoważne.


Jest to łatwe do weryfikacji za pomocą naszych własnych funkcji zamiast milionów przeciążeń dla operator<<:

void foo(bool ); // #1
void foo(std::string ); // #2

void bar(bool );  // #3
template <class C, class T, class A>
void bar(std::basic_string<C,T,A> ); // #4

foo(S{}); // error: ambiguous
bar(S{}); // calls #3
11
Barry 17 luty 2017, 21:49
ostream& operator<<( bool value );

Jest funkcją członka std::ostream. Z drugiej strony:

std::ostream& operator<<(std::ostream& os, const std::string& str);

Jest samodzielną funkcją - , która jest rzeczywiście zadeklarowana jako szablon . Odniesienie do S nie pasuje do żadnej z szablonów - więc nie jest to rozpatrywane w rozszerzeniu szablonu.


Możliwe jest jednoznaczne określenie, które przeciążenie powinno być wybrane, ale sugeruję, że tego nie zrobisz.

A) Jest to zawsze jeden z trudnych zakątków standardu (więc możesz napotkać błędów kompilatorowych;

B) przyszły deweloperzy zawsze będą trudno znaleźć kod.

Moją sugestią jest uniknięcie problemu w całości, po prostu wykonywanie operatorów konwersji lub dają im imiona jak to_bool() i to_string().

2
Martin Bonner supports Monica 15 luty 2017, 15:29