【问题标题】:Passing a nullptr to my overloaded function causes a runtime error将 nullptr 传递给我的重载函数会导致运行时错误
【发布时间】:2021-04-06 22:44:44
【问题描述】:

为了了解更多使用指令和函数重载,我尝试了这个程序:

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;
    }
}

当我运行程序时,它工作正常,除了最后一次调用f(nullptr) 导致运行时错误:

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

如果我取消注释命名空间 binusing 指令,则代码可以正常工作。

using namespace bin;

输出:

int
double
ns::Foo
bin::f(int*)

【问题讨论】:

  • void f(std::string) 将错误地尝试将char* 转换为std::string
  • 不幸的是,您可以使用f(0) 做同样的事情。任何其他数字都将被编译器捕获为非法的整数到字符串的转换,但 NULL 和 0 之间的历史关系在此阻碍了。
  • 使用命名空间 bin 时,您的 f(int*) 更匹配(无转换),然后字符串(const char*)错误这一事实并不重要。

标签: c++ overloading using-directives


【解决方案1】:

using namespace bin; 被注释掉时,只有一个可用的f() 版本可以将nullptr 作为输入。 nullptr 不能隐式转换为intdouble,因此排除了ns::f(int)ns::f(double)。但是std::string 可以从const char* 构造,并且nullptr 可以隐式转换为const char*,因此编译器可以构造一个临时的std::string 对象以传递给ns::f(std::string)。但是,未定义的行为是从一个空的 const char* 构造一个 std::string,因此会出现运行时错误(顺便说一句,这是不能保证的,因为行为是 未定义的 ,所以任何事情都可能发生)。

using namespace bin; 没有被注释掉时,有两个可用的f() 版本可以将nullptr 作为输入。 bin::f(int*)ns::f(std::string) 匹配更好,因为nullptr 可以隐式转换为int*,因此不需要构造临时对象,因此编译器选择调用bin::f(int*) 而不是ns::f(std::string)

【讨论】:

  • 好的,谢谢。所以你的意思是std::string构造函数接受一个常量字符串总是取消引用那个参数(char const*)?所以我认为那个字符串构造函数检查它的参数(指针)是否有一个空终止符作为字符串结尾的歌声,这就是为什么它取消引用它甚至是空的?因此,该 ctor 的可能实现 r 重载是提供一个单独的参数作为大小。那时我们可以决定是否取消引用该指针。 (如果 0 不取消引用,否则这样做)?我的想法对吗?
  • 并且该构造函数采用指向字符串 (std::string(char const*);) 的单个指针而不是 explicit 以允许通过复制初始化将 (char const* 到 std::string) 隐式转换为const char* 例如std::string str = "Hi there!"; ?
  • std::string(const char*) 构造函数在被赋予空指针时所做的是未定义。它可能会取消引用指针,也可能不会。这由实施来决定。如果你想避免这个问题,你可以提供一个ns::f(const char*) 重载并自己检查null。但是,如果使用using namespace bin;,则会出现歧义错误,除非您将nullptr 显式类型转换为char*int*
  • 所以您的意思是实现负责如何从指向 char char const* 的指针初始化类 std::string 的数据成员?
  • 是的,标准并没有说明std::string 构造函数必须做什么,所以实现可以为所欲为。一些实现将空指针视为长度为 0 的字符串。有些没有。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多