2013-03-04 3 views
7
#include <iostream> 
using namespace std; 

class Foo{ 

     string _s; 
     public: 
     Foo(string ss){ 
       _s = ss; 
     } 
     Foo& operator=(bool b){ 
       cout << "bool" << endl; 
       return *this; 
     } 
     Foo& operator=(const string& ss){ 
       cout << "another one" << endl; 
       return *this; 
     } 
}; 


int main(){ 

     Foo f("bar"); 
     f = "this"; 
     return 0; 

} 

Я перегрузил = оператора. Я ожидал f = "this"; заявление для звонка operator=(const string& ss) перегрузка. Но это не так. Он вызывает operator=(bool b) перегрузки. Зачем?Странное поведение при перегрузке оператора?

ответ

13

Этот оператор operator=(const string& ss) требует преобразования пользовательского определенного типа для аргумента (const char* к std::string), в то время как версия bool не имеет ни одного и так обеспечивает лучший матч: вы получаете преобразования из встроенных типов const char[5] в const char* к bool.

+0

Что? В этой перегрузке есть только один UDT. (Кроме этого, 100% правильно) –

+0

@MooingDuck Да, вы правы. Это имело смысл, когда я набрал ответ, не задумываясь! – juanchopanza

+1

+1: Это может удивить новичков языком, что тип, определенный Стандартом, будет классифицироваться как ** пользовательский ** определенный тип, но там вы идете! – Johnsyweb

0

Не вникая в стандарты C++, на первый взгляд ваша проблема заключается в том, что вы задали перегрузку назначения с помощью строки & и bool, но в вашем тесте вы назначаете массив char *.

Так что, если вы определяете строку и присваиваете ее, она будет работать как ожидалось.

Проверьте это работает здесь: http://codepad.org/owb6noXR

+1

Альтернативное решение, которое снимает ответственность с вызывающего абонента, находится здесь: http://liveworkspace.org/code/6UxaG$1, но на самом деле это не отвечает на вопрос *** почему *** эта особая перегрузка вызывается. – Johnsyweb

0

Как уже сказал, легко исправить, чтобы это просто отбрасывать вашу строку к std::string, при выполнении оператора, поэтому C++ точно знает, какие перегружать выбрать:

#include <iostream> 
using namespace std; 

class Foo{ 

     string _s; 
     public: 
     Foo(string ss){ 
       _s = ss; 
     } 
     Foo& operator=(bool b){ 
       cout << "bool" << endl; 
       return *this; 
     } 
     Foo& operator=(const string& ss){ 
       cout << "another one" << endl; 
       return *this; 
     } 
}; 


int main(){ 

     Foo f((string)"bar"); 
     f = (string)"this"; 
     return 0; 

} 
+1

Поскольку я прокомментировал другой ответ, альтернативное решение, которое снимает ответственность с вызывающего абонента, [здесь] (http://liveworkspace.org/code/6UxaG$1), но на самом деле это не отвечает на вопрос *** почему *** эта особая перегрузка вызывается. – Johnsyweb

1

Как отметил другой ответ, полукокс указателя на bool к конверсии является предпочтительным, поскольку это не влечет за определенным пользователем преобразования, в то время как std::string преобразование имеет определенный пользователь преобразование в нем.

C++ 11 предлагает возможность управления ручным типом.

template<size_t n> 
struct enumarated_enum { 
private: 
    enum empty {}; 
}; 

template<bool b, size_t n=0> 
using EnableIf = typename std::enable_if< b, typename enumerated_enum<n>::empty >::type; 

template<typename String, EnableIf< std::is_convertible< String, std::string >::value >... > 
Foo& operator=(String&& s) { 
    cout << "string overload" << "\n"; 
} 

// either this: 
template<typename Bool, EnableIf< !std::is_convertible< Bool, std::string >::value && std::is_convertible< Bool, bool >::value, 1 >... > 
Foo& operator=(Bool&& b) { 
    cout << "bool overload" << "\n"; 
} 
// or this: 
Foo& operator=(bool b) { 
    cout << "bool overload" << "\n"; 
} 

где мы сопоставляем тип идеально, если он может быть преобразован в std::string, а шаблон не соответствует, если он не может быть преобразован в std::string и других перегруженных просматривается.

Если у вас есть много типов, которые вы хотите поддерживать, вы должны использовать длинные логические формы, которые описывают, какие из перегрузок вы хотите запустить, и убедитесь, что другие не принимают его (с неконструкцией выше). Если у вас есть только два типа, второй тип может быть традиционной перегрузкой. Перегрузка шаблона получает первую трещину во всем, что не соответствует традиционной перегрузке точно ...

(Второй аргумент шаблона - это переменный список пронумерованных enum s, которые не могут существовать, цель которого состоит в том, чтобы убедиться, что два шаблона отличаются, чтобы компилятор не жаловался.)

Смежные вопросы