防止函数使用const std :: string&接受0


97

值一千字:

#include<string>
#include<iostream>

class SayWhat {
    public:
    SayWhat& operator[](const std::string& s) {
        std::cout<<"here\n"; // To make sure we fail on function entry
        std::cout<<s<<"\n";
        return *this;
    }
};

int main() {
    SayWhat ohNo;
    // ohNo[1]; // Does not compile. Logic prevails.
    ohNo[0]; // you didn't! this compiles.
    return 0;
}

将数字0传递给接受字符串的方括号运算符时,编译器不会抱怨。相反,它会在输入以下方法之前编译并失败:

terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_S_construct null not valid

以供参考:

> g++ -std=c++17 -O3 -Wall -Werror -pedantic test.cpp -o test && ./test
> g++ --version
gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)

我猜

编译器隐式地使用std::string(0)构造函数输入方法,这会在没有充分理由的情况下产生相同的问题(谷歌上述错误)。

无论如何,有没有在类方面解决此问题,因此API用户感觉不到这一点,并且在编译时检测到错误?

也就是说,添加重载

void operator[](size_t t) {
    throw std::runtime_error("don't");
}

不是一个好的解决方案。


2
编译后的代码,在Visual Studio中的ohNo [0]处引发异常,异常为“ 0xC0000005:访问冲突读取位置0x00000000”
TruthSeeker,

5
声明一个私有重载operator[]()接受一个int参数,而不定义它。
彼得

2
@Peter尽管确实注意到这是一个链接器错误,但仍然比我的错误要好。
kabanus

5
@kabanus在上述情况下,这将是编译器错误,因为运算符是私有的!链接器错误仅调用时才出现
阿空卡瓜

5
@Peter这就是在场景中没有C ++ 11可特别有趣的-而这些存在的今天甚至(实际上我在不得不面对一个项目,我失去了相当多的新功能,一些... )。
阿空加瓜

Answers:


161

之所以std::string(0)有效,是因为它0是一个空指针常量。所以0匹配带指针的字符串构造函数。然后,代码将违反不能将null指针传递给的先决条件std::string

0如果它是运行时的值,则只有文字会被解释为空指针常量,int因为您不会遇到这个问题(因为过载解析会寻找int转换)。文字也不是1问题,因为1它不是空指针常量。

由于这是一个编译时问题(字面无效值),因此您可以在编译时捕获它。添加此形式的重载:

void operator[](std::nullptr_t) = delete;

std::nullptr_t是的类型nullptr。它会匹配任何空指针常量,无论是00ULLnullptr。并且由于删除了该函数,因此在重载解析期间将导致编译时错误。


到目前为止,这是最好的解决方案,完全忘记了我可以重载NULL指针。
kabanus

在Visual Studio中,即使“ ohNo [0]”也会引发空值异常。这是否意味着特定于std :: string类的实现?
TruthSeeker

@pmp抛出的内容(如果有的话)是特定于实现的,但要点是字符串在所有它们中都是NULL指针。使用此解决方案,您将不会遇到异常部分,它将在编译时被检测到。
kabanus

18
@pmp- std::stringC ++标准不允许将null指针传递给的构造函数。这是未定义的行为,因此MSVC可以做任何喜欢的事情(例如引发异常)。
StoryTeller-Unslander Monica,

26

一种选择是声明它的private重载operator[]()接受一个整数参数,而不定义它。

该选项可与所有C ++标准(1998年以后)一起使用,与void operator[](std::nullptr_t) = delete从C ++ 11开始有效的选项不同。

使其operator[]()成为private成员将导致示例上的可诊断错误 ohNo[0],除非该表达式由成员函数或friend该类使用。

如果从成员函数或friend类的成员使用该表达式,则将编译代码,但是-由于未定义函数-通常,构建会失败(例如,由于函数未定义而导致的链接器错误)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.