14
#include <iostream>
#include <string>

struct mystruct{
     mystruct(std::string s){
        
        std::cout<<__FUNCTION__ <<" String "<<s;
    }
    
     explicit mystruct(bool s) {
        
        std::cout<<__FUNCTION__<<" Bool "<<s;
    }
};


int main()
{
    
    const char* c ="hello";
    
    mystruct obj(c);

    return 0;
}

output:

mystruct Bool 1
  1. Why const char* implicitly converted to bool rather than std::string, though constructor requires explicit type ?
  2. How the implicit conversion priority applies here?
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
TruthSeeker
  • 1,539
  • 11
  • 24
  • Because it's a pointer, and all pointers can naturally be implicitly converted to `bool` values. – Some programmer dude Mar 03 '21 at 07:53
  • @Someprogrammerdude: but the constructor is marked as `explicit`, why not it is referring to the type? – TruthSeeker Mar 03 '21 at 07:55
  • 1
    [Direct initialization](https://en.cppreference.com/w/cpp/language/direct_initialization), which you are doing, is always explicit. It would be a different matter if you did [copy initialization](https://en.cppreference.com/w/cpp/language/copy_initialization) (like `mystruct obj = c;`), or used `c` in another context where a `mystruct` object was expected (like calling a function expecting a `mystruct` object by value). – Some programmer dude Mar 03 '21 at 07:58
  • You can use `std::enable_if` to select the desired constructor in this case. – VLL Mar 03 '21 at 08:05
  • 2
    `explicit` disables implicit conversion of `X` to `mystruct` , not of implicit conversion of arguments types to constructor. – M.M Aug 25 '21 at 23:03
  • @M.M: yes I understood, earlier I had assumptions that `explicit` meant also for argument type to constructor – TruthSeeker Aug 26 '21 at 06:50

3 Answers3

15

Because the implicit conversion from const char* to bool is qualified as standard conversion, while const char* to std::string is user-defined conversion. The former has higher ranking and wins in overload resolution.

A standard conversion sequence is always better than a user-defined conversion sequence or an ellipsis conversion sequence.

BTW: mystruct obj(c); performs direct initialization, explicit converting constructors including mystruct::mystruct(bool) are considered too. As the result, c is converted to bool then passed to mystruct::mystruct(bool) as argument to construct obj.

Direct-initialization is more permissive than copy-initialization: copy-initialization only considers non-explicit constructors and non-explicit user-defined conversion functions, while direct-initialization considers all constructors and all user-defined conversion functions.

About explicit specifier,

  1. Specifies that a constructor or conversion function (since C++11) or deduction guide (since C++17) is explicit, that is, it cannot be used for implicit conversions and copy-initialization.
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • 1
    In my humble opinion that's WEIRD. What's the point of `explicit`, then? – manuell Mar 03 '21 at 07:59
  • 4
    It stops accidental conversions, e.g: https://godbolt.org/z/heErYe or https://godbolt.org/z/7hWdon, if you couldn't call an explicit constructor in the way you've written it you'd never be able to call that constructor at all – Alan Birtles Mar 03 '21 at 08:00
  • 1
    @manuell It takes effect in [copy initialization](https://en.cppreference.com/w/cpp/language/copy_initialization), but doesn't make difference in direct initialization. – songyuanyao Mar 03 '21 at 08:00
  • @AlanBirtles Not sure what you mean with second part of your comment, how about `mystruct obj( static_cast(c));` ? I think thats what OP expect to be required to call the explicit ctr – 463035818_is_not_an_ai Mar 03 '21 at 08:01
  • @largest_prime_is_463035818 I suppose they could have defined it that way, pretty awkward though – Alan Birtles Mar 03 '21 at 08:03
  • 1
    @manuell maybe it helps to think of it like that: What is explicit in the line `mystruct obj(c);` is that you are explicitly creating a `mystruct`, while you cannot call a `foo(mystruct)` with a `bool` directly. Its the creating of a `mystruct` that is explicit not the parameter type to the constructor – 463035818_is_not_an_ai Mar 03 '21 at 08:06
1

"Why const char* implicitly converted to bool rather than std::string, though constructor requires explicit type ?":

  • Standard conversion is preferred over user defined-conversions.

char const* is a pointer to constant character and a pointer can be implicitly converted to bool : in case it is nullptr it is converted to false otherwise to true.

  • You used to see such effective conversion in conditions where you check whether the pointer is NULL or not so in case it is not nulptr we safely de-reference it otherwise it has nullptr value thus it is not correct to de-reference it:

    int* ptr = nullptr;
    
    if(ptr) // false  because ptr has nullptr or NULL or 0 or 0x000000 address value
       std::cout << ptr << '\t' << *ptr << '\n'; // not executed
    
    ptr = new int(10); // valid and non-nullptr
    
    if(ptr) // non-nullptr so condition succeeds
       std::cout << ptr << '\t' << *ptr << '\n'; // 0FED155   10
     delete ptr; // free memory
    
  • explicit constructor means it can only be called explicitly and the only way is through Direct Initialization like in your case:

    mystruct obj(c); // direct initialization
    mystruct obj = c; // copy-initialization. Error: constructor myStruct(bool) is `explicit`
    
Itachi Uchiwa
  • 3,044
  • 12
  • 26
0

Just to add to the already good answer. You can work around this by adding a delegating constructor as in:

mystruct(const char* s):mystruct(std::string(s)) {}

Which will win over bool in overload resolution.

Another option is to avoid bool here and use a bool but something that carries more meaning. This could be an example of 'boolean blindness'

Bruce Adams
  • 4,953
  • 4
  • 48
  • 111