From Guru of the Week #2. We have the original function:
string FindAddr( list<Employee> l, string name )
{
for( list<Employee>::iterator i = l.begin(); // (1)
i != l.end();
i++ )
{
if( *i == name ) // (2)
{
return (*i).addr;
}
}
return "";
}
I added dummy Employee class to that:
class Employee
{
string n;
public:
string addr;
Employee(string name) : n(name) {}
Employee() {}
string name() const
{
return n;
}
operator string()
{
return n;
}
};
And got compilation error:
In place (1):
conversion from ‘std::_List_const_iterator<Employee>’ to non-scalar type ‘std::_List_iterator<Employee>’ requested
In place (2):
no match for ‘operator==’ in ‘i.std::_List_iterator<_Tp>::operator* [with _Tp = Employee]() == name’
To eliminate first one, we change iterator to const_iterator. And the only way to eliminate second error, is to write own operator==. However, Herb Sutter wrote that:
The Employee class isn’t shown, but for this to work it must either have a conversion to string or a conversion ctor taking a string.
But Employee has a conversion function and conversion constructor. GCC version 4.4.3. Compiled normally, g++ file.cpp without any flags.
There should be implicit conversion and it should work, why it doesn’t? I don’t want operator==, I just want it work like Sutter said, with a conversion to string or a conversion ctor taking a string.
Herb Sutter is wrong in this case (I don’t have a copy of “Exceptional C++”, but I’d expect this GotW entry to be cleaned up for the book.)
But first, in order to get to the error in question, you have to remove the
constfrom thelparameter declaration. (Note, that replacingiteratorwithconst_iteratorwill only obfuscate the problem: youroperator string()is notconst, meaning that it is not callable for constant object*i).Once you fix the first problem, your code will indeed fail to compile at
line. This happens because function
std::operator ==that comparesstd::stringobjects is actually defined by the Standard Library as a template functionIn order for this function to participate in overload resolution, its template arguments have to be successfully deduced. This is impossible in your context, since in
*i == nameone argument isstd::stringand another isEmployee. Template argument deduction fails and, for this reason, this template function is not considered for overload resolution. Having no other candidates, the compiler reports an error.For this reason Herb Sutter’s claim that the code should be compilable in presence of
operator string()conversion function inEmployeeclass is incorrect. The code might compile with some specific implementation of Standard Library that declares a dedicated non-template comparison operator forstd::string, but normally Standard Library implementations don’t do it that way.He also makes another unfounded claim, insisting that the result of that conversion must be a temporary. In reality
Employeeclass can haveoperator const string &() constconversion function, which would create no temporaries (return a reference to a data member instead, as it could be done in your example).Finally, his claim that conversion constructor will make this code work is only true if the program declares a dedicated
operator ==forEmployee vs. Employeecomparisons. Without introducing such dedicated operator, the conversion constructor will have no effect on the validity of this code. I.e. in your example there was no point in declaringEmployee(string name)constructor – it does not achieve anything.