Гуру недели № 2 для оператора не подходит ==

От Гуру недели № 2. У нас есть оригинальная функция:

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 "";
}

Я добавил фиктивный класс Employee к этому:

class Employee
{
string n;
public:
string addr;

Employee(string name) : n(name) {}
Employee() {}

string name() const
{
return n;
}

operator string()
{
return n;
}
};

И получил ошибку компиляции:

На месте (1):

conversion from ‘std::_List_const_iterator<Employee>’ to non-scalar type ‘std::_List_iterator<Employee>’ requested

На месте (2):

no match for ‘operator==’ in ‘i.std::_List_iterator<_Tp>::operator* [with _Tp = Employee]() == name’

Чтобы устранить первый, мы меняем iterator в const_iterator, И единственный способ устранить вторую ошибку — написать собственный оператор ==. Тем не менее, Херб Саттер писал, что:

Класс Employee не отображается, но чтобы это работало, он должен иметь либо преобразование в строку, либо преобразователь ctor, принимающий строку.

Но у Employee есть функция преобразования и конструктор преобразования. GCC версия 4.4.3. Скомпилировано нормально, g++ file.cpp без каких-либо флагов.

Должно быть неявное преобразование, и оно должно работать, почему это не так? Я не хочу оператора ==, я просто хочу, чтобы он работал, как сказал Саттер, с преобразование в строку или преобразователь ctor, принимающий строку.

2

Решение

Херб Саттер ошибается в этом случае (у меня нет копии «Исключительного C ++», но я ожидаю, что эта запись GotW будет очищена для книги.)

Но сначала, чтобы добраться до рассматриваемой ошибки, необходимо удалить const от l объявление параметров. (Обратите внимание, что замена iterator с const_iterator только запутает проблему operator string() не является constЭто означает, что он не вызывается для постоянного объекта *i).

Как только вы исправите первую проблему, ваш код действительно не сможет скомпилироваться в

if( *i == name )

линия. Это происходит потому, что функция std::operator == что сравнивает std::string объекты на самом деле определяется стандартной библиотекой как шаблон функция

template<class charT, class traits, class Allocator>
bool operator==(
const basic_string<charT,traits,Allocator>& lhs,
const basic_string<charT,traits,Allocator>& rhs);

Чтобы эта функция участвовала в разрешении перегрузки, ее аргументы шаблона должны быть успешно выведены. Это невозможно в вашем контексте, так как в *i == name один аргумент std::string а другой Employee, Сбой вывода аргумента шаблона, и по этой причине эта функция шаблона не рассматривается для разрешения перегрузки. Не имея других кандидатов, компилятор сообщает об ошибке.

По этой причине заявление Херба Саттера о том, что код должен быть компилируемым при наличии operator string() функция преобразования в Employee класс неверен. Код может скомпилироваться с какой-то конкретной реализацией стандартной библиотеки, которая объявляет специальный оператор сравнения без шаблона для std::string, но обычно реализации стандартной библиотеки не делают этого таким образом.

Он также делает еще одно необоснованное утверждение, настаивая на том, что результат этого обращения должен быть временным. В действительности Employee класс может иметь operator const string &() const функция преобразования, которая не создаст временные объекты (вместо этого верните ссылку на элемент данных, как это можно сделать в вашем примере).

Наконец, его утверждение, что конструктор преобразования заставит этот код работать, верно только в том случае, если программа объявляет выделенный operator == за Employee vs. Employee сравнения. Без введения такого выделенного оператора конструктор преобразования не будет влиять на достоверность этого кода. То есть в вашем примере не было никакого смысла декларировать Employee(string name) конструктор — ничего не добивается.

3

Другие решения

Я считаю, что код разрешено компилировать, но не обязательно. Помни что string является typedef для basic_string<char>это не класс сам по себе. Минимальный пример:

template <typename T>
struct basic_string
{
};

typedef basic_string<char> string;

struct Employee
{
operator string() const;
};

template <typename T>
bool operator==(const basic_string<T>& a, const basic_string<T>& b);

// Compilation succeeds when this is uncommented
// bool operator==(const basic_string<char>& a, const basic_string<char>& b);

bool f(const Employee& e, const string& s)
{
return e == s; // error
}

Да, Employee конвертируется в string, но это не так действительно string, так что это не достаточно, чтобы определить, какой аргумент шаблона использовать для operator==,

Если дополнительная перегрузка специально для operator==(const string&, const string&) добавлено, это работает, и другая реализация стандартной библиотеки может обеспечить именно эту перегрузку. Если он предоставлен, код скомпилируется, но это расширение не требуется стандартом C ++.

редактировать: на самом деле, как уже упоминали другие, этого не достаточно (const проблемы), но даже если устранить другие проблемы, это остается, и я считаю, что это ответ на ваш основной вопрос.

3

отказ: код вопроса уже изменился к тому времени, как я написал и опубликовал это. Код больше не является цитатой из кода GOTW. Это другой код, и предполагаемая ошибка компиляции, по-видимому, также неверна, но я оставляю этот ответ (на исходную публикацию) в силе, поскольку он в основном касается других вопросов (я не собираюсь гоняться за серией правок в вопросе, соответственно редактируя и дорабатываю этот ответ).

@Vaibhav имеет уже ответил основной вопрос, а именно, что преобразование должно быть явно выражено.

Но так как цитируемый GOTW (Гуру Недели) касается ненужных временных, ваш класс Employee код,

class Employee
{
string n;
public:
string addr;

Employee(string name) : n(name) {}
Employee() {}

string name() const
{
return n;
}

operator string()
{
return n;
}

};

повторить некоторые подводные камни, обсуждаемые в этом GOTW.

В

Employee(string name) : n(name) {}

Взятие строкового аргумента по значениям прекрасно для C ++ 11, потому что копия все равно будет создана. Но тогда ты должен move это значение в член,

Employee(string name) : n(move(name)) {}

Тогда ваш

operator string()
{
return n;
}

страдает от не быть constтак что его нельзя вызвать на const объект, так что этот объект будет без необходимости копировать для вызова этого оператора.

Так что, технически, сделать

operator string() const
{
return n;
}

Но на уровне дизайна даже это неправильно. Сотрудник не строка. Для какой строки можно ожидать, что сотрудник будет конвертировать в? Его или ее имя? Код сотрудника? Номер социального страхования?

Неявные преобразования, как правило, проблематичны, и это тому пример. Это не помогает сделать это explicit, Поскольку соответствующая строка уже доступна через именованную операцию (что хорошо), это значительно улучшит класс Удалить это оператор преобразования.

1

Вы получаете объект Employee от итератора, поэтому * i указывает на объект Employee. Затем вам нужно указать на имя этого сотрудника.

Попробуй это:

if( (*i).name == name )
0

Что происходит, когда вы меняете операнды операторов сравнения? Компилятор жалуется, что нет operator == за List<>, но это определено для string,

изменения if( *i == name ) в if( name == *i ) должно сработать.

-1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector