The std::bitset has several constructors. One of them (3) has the form:

 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')); 

Other (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')); 

Why do you need a constructor (4), when there is (3)?

And in particular, why use CharT* instead of std::basic_string ?

  • 2
    and there is not written? Similar to (3), but uses a CharT* instead of a std::basic_string - pavel
  • one
    @pavel "why"! = "how different" - poop
  • here you can read what const char* gcc.1065356.n8.nabble.com/… specializations are for - user2354841
  • So what is the essence of the question? The author assumes that, if necessary, the functionality of option 4 will be covered by the functionality of option 3 due to the (explicit or implicit) conversion of the C-string to the type std::string ? Or the crux of the matter in something else? - AnT

2 answers 2

I think this is due to the inclusion of a new feature in the C ++ standard as an argument to use the initialization list in curly brackets.

The point is that all class constructors are declared with the explicit function specifier in order to prevent implicit conversion of other types of objects to class objects.

In this case, if you call a class constructor with an argument in curly brackets with one type, then there will be no conversion of initialization list objects to another type.

Compare these two demonstration programs.

 #include <iostream> #include <string> struct A { explicit A( const std::string & ) {} }; int main() { A a( "HEllo" ); return 0; } 

This program will compile successfully. The constructor argument from the literal type is converted to an object type of the class std::string .

Now enclose the constructor call argument in braces.

 #include <iostream> #include <string> struct A { explicit A( const std::string & ) {} }; int main() { A a( { "HEllo" } ); return 0; } 

This program will no longer compile.

Therefore, if you include another constructor with the explicit specifier, the program will already be compiled

 #include <iostream> #include <string> struct A { explicit A( const std::string & ) {} explicit A( const char * ) {} }; int main() { A a( { "HEllo" } ); return 0; } 
  • why does the transformation become possible? we have an explicit worth ... or can we implicitly convert {T} to T? - poop
  • @poop Here is the direct initialization of the argument parameter constructor. If you wrote, for example, A a = "Hello"; then you would already have an error message. - Vlad from Moscow
  • Can you give a link, where to read about this initialization? - poop
  • @poop You need to look at the section of the standard Initializers 8.5. Search the Internet preferably for the last working draft of the C ++ standard - Vlad from Moscow
  • thanks will watch - poop

The essence of having a constructor (4), as well as replacing std::basic_string with const charT* is in the overloading mechanism. And the constructor, as is known, is also chosen on the basis of the arguments passed to it.

To understand the essence, you can replace constructors with ordinary functions, remove unnecessary parameters and simplify types. In this case, we can get a couple of functions of the form:

 void b(const std::string& str, int pos = 0, int n = 100500); // 1 void b(const char* str, int n = 100500); // 2 

You may notice that functions have default parameters, i.e. function (1) can be called using 1-3 parameters, and function (2) using 1-2 parameters.

It is known that the default values ​​can be given only to the last parameters of the function. Those. if it is important for us to set only pos and leave n by default - we use version (1), and if we set only n , but leave pos - version (2).

And here it is important that the first (mandatory) parameter has a different type in different overloads, since if version (2) instead of const char* would have const std::string& there would be an ambiguity in the choice of overload, for example, for a call like:

 b("str", 10); 

But since the types are different, it is the version (2) that is called .

  • Good explanation. - Vlad from Moscow
  • @VladfromMoscow still need to bring compilers to clear in your version. - αλεχολυτ
  • Yes, it is desirable. - Vlad from Moscow