Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Uniknięcie dwuznaczności przy tworzeniu szablonu operatora dodawania dla klasy std::string

Ostatnio zmodyfikowano 2018-01-26 15:30
Autor Wiadomość
latajacaryba
Temat założony przez niniejszego użytkownika
Uniknięcie dwuznaczności przy tworzeniu szablonu operatora dodawania dla klasy std::string
» 2018-01-26 00:45:07
Witam. Próbuję w celach szkoleniowych stworzyć szablon operatora+ dla klasy std::string:
C/C++
template < typename T >
std::string operator +( const std::string & str, const T & obj )
{
    return str + std::to_string( obj );
}
Jedyny problem to char[]. Chodzi o to, że jak wiecie, klasa string nie ma zdefiniowanego operatora dla np. string + int, więc podstawiając pod
T
 
int
 mamy poprawne przeładowanie. Natomiast problem zaczyna sie tu:
C/C++
int main()
{
    std::string str = "Ala ma ";
    std::cout << str + " 5 kotow\n";
}

Ponieważ mamy dwa operatory + dla string i char[] - jeden dostarczany wraz z klasą, a drugi to mój.

Natomiast nie wiem, dlaczego działa coś takiego (a analogicznie - nie powinno)
C/C++
template < typename T >
void f( T a ) { cout << "inny\n"; }
void f( int a ) { cout << "int\n"; }

int main()
{
    f( 10 )
}

Dlatego też domyślam się, że błąd tkwi po mojej stronie, nie dwóch wersji operatora. Tylko gdzie?

logi:


error: ambiguous overload for 'operator+' (operand types are 'std::string {aka std::basic_string<char>}' and 'const char [3]')|
note: candidates are:|
std::string operator+(const string&, const T&) [with T = char [3]; std::string = std::basic_string<char>]|
C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32\4.9.2\include\c++\bits\basic_string.h|2485|note: std::basic_string<_CharT, _Traits, _Alloc> std::operator+(std::basic_string<_CharT, _Traits, _Alloc>&&, const _CharT*) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>] <near match>|
C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32\4.9.2\include\c++\bits\basic_string.h|2485|note:   no known conversion for argument 1 from 'std::string {aka std::basic_string<char>}' to 'std::basic_string<char>&&'|
C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32\4.9.2\include\c++\bits\basic_string.h|2421|note: std::basic_string<_CharT, _Traits, _Alloc> std::operator+(const std::basic_string<_CharT, _Traits, _Alloc>&, const _CharT*) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]|

P-169003
pekfos
» 2018-01-26 01:38:21
Natomiast nie wiem, dlaczego działa coś takiego (a analogicznie - nie powinno)
C/C++
template < typename T >
void f( T a ) { cout << "inny\n"; }
void f( int a ) { cout << "int\n"; }

int main()
{
    f( 10 )
}
To jest przeciążenie funkcji, raz dla int, raz uniwersalne dla T. Jawnie podany typ ma pierwszeństwo nad szablonem.

Zobacz teraz co mówi komunikat:
note: candidates are:
..
note: std::basic_string<_CharT, _Traits, _Alloc> std::operator+(const std::basic_string<_CharT, _Traits, _Alloc>&, const _CharT*) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]|
Wersja dla const char* jest też funkcją, a nie metodą klasy, ale to funkcja w przestrzeni nazw std, więc nie przeciążasz jej, tylko dodajesz konkurencyjną funkcję powodującą niejednoznaczność. Przeciążyć też tego nie możesz.

To co możesz zrobić, to uniemożliwić dopasowanie twojego szablonu do rzeczy, które pasują do standardowych operatorów dla std::string. Lub prościej, umożliwić dopasowanie tylko do rzeczy, które pasują do std::to_string():
C/C++
template < typename T, typename = std::enable_if_t < std::is_arithmetic_v < T >>>
std::string operator +( const std::string & str, T obj )
{
    return str + std::to_string( obj );
}
P-169005
latajacaryba
Temat założony przez niniejszego użytkownika
» 2018-01-26 03:22:39
W sumie pierwszy raz mam z czymś takim do czynienia. Gdyby ktoś mnie o to wcześniej zapytał, strzelałbym, że kompilator wybierze sobie jedną z dwóch funkcji, mimo, że jedna nie przeciąża drugiej. Cóż, dzięki w takim razie za poprawną formę, jedna rzecz tylko nie da mi spać w nocy ;) co oznacza zapis
typename = std::enable_if_t < std::is_arithmetic_v < T >>
Jest to jakiś beznazwowy typ szablonowy, któremu przypisujesz... no właśnie. std::enable_if_t wygląda jak alias na zmienną (type enable_if<B,T>::type), ale nie jestem pewien, czy to alias (znam aliasy szablonów, ale tu jest jeszcze ::value) . value jest typu takiego samego, jaki typ ma składnik który przekazaliśmy jako drugi
template < bool B, class T = void >
 (domyślnie void, tu T) lub go nie ma(czyli jest typu void?) - zależne od B. A więc skoro tym B jest
std::is_arithmetic_v < T >
 to jakoś od niego jest to zależne. Wiem, że is_arithmetic sprawdza, czy podany typ jest tym arytmetycznym i jego składnik value przybiera true/false, ale nie wiem co robi is_arithmetic_v. Wygląda troche jak zmienna, ale ma inline charakterystyczne dla funkcji. Ale to mniejszy problem, bardziej zastanawia mnie, po co jest to całe
typename = std::enable_if_t < std::is_arithmetic_v < T >>
 Przecież ten bezimienny typ nie jest nigdzie używany.
P-169007
pekfos
» 2018-01-26 15:08:32
bardziej zastanawia mnie, po co jest to całe
typename = std::enable_if_t < std::is_arithmetic_v < T >>
 Przecież ten bezimienny typ nie jest nigdzie używany.
Nie musi być używany. Przeanalizuj dokładnie, czym jest std::enable_if<>::type. Ten typ jest zdefiniowany tylko dla std::enable_if<true>, dla false nie ma tam żadnego type, więc dla typów niespełniających std::is_arithmetic<> ten zapis się nie kompiluje. Nie powoduje to błędu kompilacji, tylko odrzucenie przeciążenia ze zbioru kandydatów, w myśl zasady Substitution Failure Is Not An Error.
P-169017
latajacaryba
Temat założony przez niniejszego użytkownika
» 2018-01-26 15:30:21
Okej, poukładam to sobie, doczytam i pobawię się tym trochę. Dzięki za pomoc, temat zamykam :)
P-169020
« 1 »
  Strona 1 z 1