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