Вставка объектов в вектор с использованием lower_bound с compare struct

#c #vector #insert #constants #lower-bound

#c #вектор #вставить #константы #нижняя граница

Вопрос:

У меня есть этот класс:

 class Mail {
  public:
    Mail(const string amp; msg) : msg(msg) {}

    const string msg;
};
  

И эта структура, которая сравнивает два почтовых объекта:

 struct Compare {
  bool operator()(const Mail amp; mail, Mail const amp; mail2) const {
    return mail.msg < mail2.msg;
  }
};
  

Я хочу иметь вектор с почтовыми объектами, отсортированными по их сообщению const string msg . Однако, когда я пытаюсь вставить новый объект в вектор с помощью lower_bound , я получаю много ошибок, в том числе:

передача ‘const string как ‘this’ аргумент отбрасывает квалификаторы.

 int main() {
  vector <Mail> mails;

  Mail mail2("1");
  mails.push_back(mail2);

  const string msg = "2";
  Mail mail(msg);
  auto low = lower_bound(mails.begin(), mails.end(), mail, Compare());

  // mails.push_back(mail);   // OK
  mails.insert(low, mail); // passing ‘const string as ‘this’ argument discards qualifiers

  return 0;
}
  

Я пока не очень разбираюсь const в использовании и не могу понять, что const неправильно.
Прошу прощения, если об этом уже спрашивали, но я пока не нашел ответа на эту проблему.

Комментарии:

1. вы уверены, что хотите string msg быть const ? Это означает, что его нельзя изменить после инициализации, и именно это мешает вам вставить его.

2. Спасибо за все ответы, теперь я понимаю проблему. Однако я не могу изменить class Mail включение const string msg . Я изменил вектор на вектор указателей vector<CMail*> .

Ответ №1:

Ошибки в C иногда бывает трудно диагностировать. Мой совет — всегда начинать с самого верха и сначала разрешить его. В этом случае их длинный список, но все они на самом деле об одном и том же — оператор присваивания для Mail не может быть сгенерирован.

Подумайте об этом так: компилятор полезен и пытается сгенерировать (и внутри lower_bound() , использовать) эту функцию:

 Mailamp; operator=( constamp; Mail mail ) 
{ 
    msg = mail.msg; 
    return *this;
}
  

Но это невозможно, потому что это назначение в теле недопустимо из-за msg того const , что . Вы также не можете написать это самостоятельно, поскольку вы также не можете назначить const переменной.

Обычно вам не нужны переменные-члены const , потому что они становятся const , если экземпляр класса сам const :

 const auto mail1 = Mail{"1"};
auto       mail2 = Mail{"2"};

mail1.msg = "3"; // FAIL! msg is const since mail1 is const
mail2.msg = "4"; // Ok! msg is not const
  

Если вам действительно нужен const член, вы не можете использовать операторы присваивания с классом. Это разрывы.

Удалите это const , и все работает:

 #include <vector>
#include <string>
#include <algorithm>

using namespace std;

class Mail {
  public:
    Mail(const string amp; msg) : msg(msg) {}

    string msg; //////////////////////////////// Not const!
};

struct Compare {
  bool operator()(const Mail amp; mail, Mail const amp; mail2) const {
    return mail.msg < mail2.msg;
  }
};

int main() {
  vector <Mail> mails;

  Mail mail2("1");
  mails.push_back(mail2);

  const string msg = "2";
  Mail mail(msg);
  auto low = lower_bound(mails.begin(), mails.end(), mail, Compare());

  // mails.push_back(mail);   // OK
  mails.insert(low, mail); // OK!

  return 0;
}
  

Посмотрите, как он работает в реальном Coliruвремени.

Сноски:

  • Вы могли бы использовать лямбда-выражение для сопоставителя, чтобы избежать некоторых шаблонов вокруг класса Compare :
 const auto low = lower_bound( begin(mails), end(mails), mail, 
                              []( const autoamp; mail1, const autoamp; mail2 ) 
                              { return mail1.msg < mail2.msg; } );
  
  • Вы можете использовать vector::emplace_back() для создания элементов на месте, избегая копирования. Следующие блоки выполняют то же самое, но второй более эффективен:
 const auto mail = Mail{"2"};
mails.push_back( mail2 ); // Copies

mails.emplace_back("2"); // Creates it right in the vector
  
  • Рассмотрите возможность использования vector::reserve() , если вы знаете, сколько элементов вы поместите в свой вектор.

Ответ №2:

Проблемы здесь связаны с удаленным оператором присваивания копирования и удаленным оператором присваивания перемещения из-за const string msg; члена в Mail классе:

Удален неявно объявленный оператор присваивания копирования

Оператор присваивания копирования по умолчанию для класса T определяется как удаленный, если выполняется любое из следующих действий:

  • T имеет нестатический элемент данных неклассового типа (или его массив), который const ;

Удален неявно объявленный оператор присваивания перемещения

Неявно объявленный или используемый по умолчанию оператор присваивания перемещения для класса T определяется как удаленный, если выполняется любое из следующих действий:

  • T имеет нестатический элемент данных, который const ;