1

To understand more using directives and function overloading I've tried this program:

namespace ns{
    void f(int){cout << "int\n";}
    void f(double){cout << "double\n";}
    void f(std::string){cout << "string\n";}
    struct Foo{};
}

void f(ns::Foo const&){
    cout << "ns::Foo\n";
}

namespace bin{
    void f(int*){
        std::cout << "bin::f(int*)\n";
    }
}


int main(){

    using namespace ns;
    //using namespace bin;

    f(7); // int
    f(7.5); // double

    f(ns::Foo{}); // ns::Foo

    try{
        f(nullptr);
    }
    catch(std::exception const& e){
        std::cout << e.what() << std::endl;
    }
}

When I run the program, it works fine, except for the last call to f(nullptr) which causes a runtime error:

int
double
ns::Foo
basic_string::_M_construct null not valid

If I un-comment the using directive for namespace bin, then the code works fine.

using namespace bin;

The output:

int
double
ns::Foo
bin::f(int*)
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Itachi Uchiwa
  • 3,044
  • 12
  • 26
  • 2
    `void f(std::string)` will erroneously try and convert `char*` to `std::string`. – tadman Apr 06 '21 at 22:48
  • You can unfortunately do the same thing with `f(0)`. Any other number will be caught by the compiler as an illegal integer-to-string conversion, but the historical relationship between `NULL` and 0 gets in the way here. – user4581301 Apr 06 '21 at 22:55
  • 2
    when using namespace bin your f(int*) is better match (no conversion) and then the fact that string( const char* ) errors does not matter. – engf-010 Apr 06 '21 at 23:12

1 Answers1

4

When using namespace bin; is commented out, there is only 1 version of f() available that can take a nullptr as input. nullptr is not implicitly convertible to int or double, so ns::f(int) and ns::f(double) are ruled out. But std::string can be constructed from a const char*, and nullptr is implicitly convertible to const char*, so the compiler can construct a temporary std::string object to pass to ns::f(std::string). However, it is undefined behavior to construct a std::string from a null const char*, hence the runtime error (which is NOT guaranteed, BTW, as the behavior is undefined, so anything can happen).

When using namespace bin; is not commented out, there are 2 versions of f() available that can take a nullptr as input. bin::f(int*) is a better match than ns::f(std::string), as nullptr is implicitly convertible to int*, so there is no need to construct a temporary object, thus the compiler chooses to call bin::f(int*) instead of ns::f(std::string).

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Ok Thank you. So you mean that `std::string` constructor that takes a constant character string always de-reference that argument (`char const*`)? So I think that that string constructor checks its argument (pointer) for a null-terminator as a sing of end of string that is why it de-reference it even being null? So a possible implementation r overload of that ctor is to provide a separate argument as the size. At that point we can decide to de-reference or not that pointer. (if 0 don't de-reference otherwise do it)? are my thought correct? – Itachi Uchiwa Apr 07 '21 at 20:57
  • And that constructor takes a single pointer to character string (`std::string(char const*);`) and is not `explicit` to allow Implicit Conversion for (char const* to std::string) through Copy-Initialization for a const char* e.g `std::string str = "Hi there!";` ? – Itachi Uchiwa Apr 07 '21 at 20:59
  • 1
    What the `std::string(const char*)` constructor does when it is given a null pointer is **undefined**. It MAY dereference the pointer, or it MAY NOT. That is up to the implementation to decide. If you want to avoid this issue, you could provide an `ns::f(const char*)` overload and check for null yourself. But then you will have an ambiguity error if `using namespace bin;` is used, unless you explicitly typecast the `nullptr` to `char*` to `int*`. – Remy Lebeau Apr 07 '21 at 21:15
  • So you mean the implementation is responsible on how to initialize data members of class `std::string` from a pointer to char `char const*`? – Itachi Uchiwa Apr 07 '21 at 21:16
  • 1
    Yes, the standard does not say what the `std::string` constructor must do, so the implementation can do whatever it wants. Some implementations treat a null pointer as a 0-length string. Some don't. – Remy Lebeau Apr 07 '21 at 22:13