Skip to content

Latest commit

 

History

History
77 lines (57 loc) · 3.93 KB

stl_constructors.md

File metadata and controls

77 lines (57 loc) · 3.93 KB

Перегруженные конструкторы стандартной библиотеки

При проектировании стандартной библиотеки C++ было принято множество странных решений, из-за которых приходится страдать. И исправить их не представляется возможным из-за соображений обратной совместимости.

Одним из таких странных решений являются перегрузки конструкторов с радикально различным поведением.

Яркий пример:

using namespace std::string_literals;
std::string s1 { "Modern C++",  3 };
std::string s2 { "Modern C++"s, 3 };

std::cout << "S1: " << s1 << "\n";
std::cout << "S2: " << s2 << "\n";

Этот код выведет

S1: Mod
S2: ern C++

Потому что у std::basic_string есть один конструктор, принимающий указатель и длину строки. А есть еще один конструктор, принимающий «что-то похожее на строку» и позицию, с которой надо из нее извлечь подстроку!

На этом причуды не заканчиваются.

std::string s1 {'H', 3};
std::string s2 {3, 'H'};
std::string s3 (3, 'H');

std::cout << "S1: " << s1.size() << "\n";
std::cout << "S2: " << s2.size() << "\n";
std::cout << "S3: " << s3.size() << "\n";

Этот пример выведет

S1: 2
S2: 2
S3: 3

Потому что у строки есть конструктор, принимающий число n и символ c, который нужно повторить n раз. А еще есть конструктор, принимающий список инициализации (std::initializer_list<T>), состоящий из символов. И существование этого конструктора взаимодействует с неявным приведением типов!

  • std::string s1 {'H', 3}; — строка "H\3"
  • std::string s2 {3, 'H'}; — строка "\3H"
  • std::string s3 (3, 'H'); — строка "HHH"

Аналогичной проблемой страдает std::vector

std::vector<int> v1 {3, 2}; // v1 == {3, 2}
std::vector<int> v2 (3, 2); // v2 == {2,2,2}

А еще у контейнеров есть конструктор, принимающий пару итераторов. И, казалось бы, с ними уж проблем-то не будет, но у нас есть указатели, которые также являются итераторами. А еще есть тип bool:

bool array[5] = {true, false, true, false, true};
std::vector<bool> vector {array, array + 5};
std::cout << vector.size() << "\n";

Будет выведено 2, а не 5. Потому что указатели неявно приводятся к bool!

Собственно, эти прекрасные примеры показывают, почему «универсальная» инициализация не универсальная.

Чтобы не множить хаос в своих проектах, нужно быть осторожнее с объявлениями перегруженных конструкторов для своих типов. Лучше ввести статическую функцию, чем создавать перегруженные конструкторы, неожиданно взаимодействующие с неявным приведением типов и списками инициализации.

Полезные ссылки

  1. https://habr.com/ru/post/330402/