Класс std::bitset
имеет несколько конструкторов. Один из них (3) имеет вид:
template< class CharT, class Traits, class Alloc >
explicit bitset( const std::basic_string<CharT,Traits,Alloc>& str,
typename std::basic_string<CharT,Traits,Alloc>::size_type pos = 0,
typename std::basic_string<CharT,Traits,Alloc>::size_type n =
std::basic_string<CharT,Traits,Alloc>::npos,
CharT zero = CharT('0'),
CharT one = CharT('1'));
Другой (4):
template< class CharT >
explicit bitset( const CharT* str,
typename std::basic_string<CharT>::size_type n =
std::basic_string<CharT>::npos,
CharT zero = CharT('0'),
CharT one = CharT('1'));
Зачем нужен конструктор (4), когда есть (3)?
И в частности, зачем использовать CharT*
вместо std::basic_string
?
Я думаю, что это связано с включенной новой возможностью в стандарт C++ в качестве аргумента использовать список инициализации в фигурных скобках.
Дело в том, что все конструкторы класса объявлены со спецификатором функции explicit
, чтобы предотвратить неявное преобразование объектов других типов в объекты класса.
В этом случае если вы вызовите конструктор класса с аргументом в фигурных скобках с одним типом, то не будет преобразования объектов списка инициализации в другой тип.
Сравните эти две демонстрационные программы
#include <iostream>
#include <string>
struct A
{
explicit A( const std::string & ) {}
};
int main()
{
A a( "HEllo" );
return 0;
}
Данная программа будет успешно компилироваться. Аргумент конструктора из типа литерала преобразуется в тип объекта класса std::string
.
Теперь заключите аргумент вызова конструктора в фигурные скобки
#include <iostream>
#include <string>
struct A
{
explicit A( const std::string & ) {}
};
int main()
{
A a( { "HEllo" } );
return 0;
}
Данная программа уже не будет компилироваться.
Поэтому если вы включите еще один конструктор со спецификатором explicit
, то программа уже будет компилироваться
#include <iostream>
#include <string>
struct A
{
explicit A( const std::string & ) {}
explicit A( const char * ) {}
};
int main()
{
A a( { "HEllo" } );
return 0;
}
Суть наличия конструктора (4), а также замены std::basic_string
на const charT*
кроется в механизме перегрузке функций. А конструктор, как известно, тоже выбирается на основании переданных в него аргументов.
Для понимания сути можно заменить конструкторы на обычные функции, убрать лишние параметры и упростить типы. В таком случае мы можем получить пару функций вида:
void b(const std::string& str, int pos = 0, int n = 100500); // 1
void b(const char* str, int n = 100500); // 2
Можно заметить, что функции имеют параметры по умолчанию, т.е. функцию (1) можно вызвать использую 1-3 параметра, а функцию (2), используя 1-2 параметра.
Известно, что значения по умолчанию можно давать только последним параметрам функции. Т.е. если нам важно задать только pos
и оставить n
по умолчанию - мы используем версию (1), а если задать только n
, но оставить pos
- версию (2).
И вот тут важно, чтобы первый (обязательный) параметр имел в разных перегрузках разных тип, т.к. если бы версия (2) вместо const char*
имела бы const std::string&
возникла бы неоднозначность выбора перегрузки например для вызова вида:
b("str", 10);
Но так как типы разные - вызывается именно версия (2).
Виртуальный выделенный сервер (VDS) становится отличным выбором
Как поместить в data_new объект data_old без копирования последнего, те