简体   繁体   English

防止 function 接受 const std::string& 接受 0

[英]Prevent function taking const std::string& from accepting 0

Worth a thousand words:值得一千字:

#include<string>
#include<iostream>

class SayWhat {
    public:
    SayWhat& operator[](const std::string& s) {
        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;
}

The compiler is not complaining when passing the number 0 to the bracket operator accepting a string.将数字 0 传递给接受字符串的括号运算符时,编译器不会抱怨。 Instead, this compiles and fails before entry to the method with:相反,这会在进入方法之前编译并失败:

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

For reference:以供参考:

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

My guess我猜

The compiler is implicitly using the std::string(0) constructor to enter the method, which yields the same problem (google the above error) for no good reason.编译器隐式使用std::string(0)构造函数进入该方法,这会产生同样的问题(谷歌上述错误),没有充分的理由。

Question问题

Is there anyway to fix this on the class side, so the API user does not feel this and the error is detected at compile time?有没有办法在 class 端解决这个问题,所以 API 用户感觉不到这个并且在编译时检测到错误?

That is, adding an overload也就是说,添加一个重载

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

is not a good solution.不是一个好的解决方案。

The reason std::string(0) is valid, is due to 0 being a null pointer constant. std::string(0)有效的原因是0是 null 指针常量。 So 0 matches the string constructor taking a pointer.所以 0 匹配带有指针的字符串构造函数。 Then the code runs afoul of the precondition that one may not pass a null pointer to std::string .然后代码运行与不能将 null 指针传递给std::string的前提条件相冲突。

Only literal 0 would be interpreted as a null pointer constant, if it was a run time value in an int you wouldn't have this problem (because then overload resolution would be looking for an int conversion instead).只有文字0会被解释为 null 指针常量,如果它是int中的运行时值,则不会遇到此问题(因为重载解析将改为寻找int转换)。 Nor is literal 1 a problem, because 1 is not a null pointer constant.文字1也不是问题,因为1不是 null 指针常量。

Since it's a compile time problem (literal invalid values) you can catch it at compile time.由于这是一个编译时问题(文字无效值),您可以在编译时捕获它。 Add an overload of this form:添加此表单的重载:

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

std::nullptr_t is the type of nullptr . std::nullptr_tnullptr的类型。 And it will match any null pointer constant, be it 0 , 0ULL , or nullptr .它将匹配任何null 指针常量,无论是00ULL还是nullptr And since the function is deleted, it will cause a compile time error during overload resolution.并且由于function被删除,在重载解析时会造成编译时错误。

One option is to declare a private overload of operator[]() that accepts an integral argument, and don't define it.一种选择是声明operator[]()private重载,它接受一个整数参数,并且不定义它。

This option will work with all C++ standards (1998 on), unlike options like void operator[](std::nullptr_t) = delete which are valid from C++11.此选项适用于所有 C++ 标准(1998 年起),不像void operator[](std::nullptr_t) = delete等选项在 C++11 中有效。

Making the operator[]() a private member will cause a diagnosable error on your example ohNo[0] , unless that expression is used by a member function or friend of the class.operator[]()设为private成员将导致示例ohNo[0]出现可诊断错误,除非该表达式由成员 function 或 class 的friend使用。

If that expression is used from a member function or friend of the class, the code will compile but - since the function is not defined - generally the build will fail (eg a linker error due to an undefined function). If that expression is used from a member function or friend of the class, the code will compile but - since the function is not defined - generally the build will fail (eg a linker error due to an undefined function).

Using string_view helps (somewhat)使用 string_view 有帮助(有点)

As of C++17, we have the std::string_view class .从 C++17 开始,我们有std::string_view class It is intended exactly for this use-case, of passing non-owning references-to-string-like-objects, to functions which only read a string.它完全适用于这种用例,将非拥有的对类字符串对象的引用传递给只读取字符串的函数。 You should seriously consider using it for this kind of operators.您应该认真考虑将其用于此类运算符。

Now, std:: string_view has its own set issues (See: enough string_view to hang ourselves with ), but here it will give you a useful warning.现在, std:: string_view有它自己的设置问题(请参阅:足够的string_view to hang 我们自己),但在这里它会给你一个有用的警告。 If you replace:如果您更换:

    SayWhat& operator[](const std::string& s) {

with

    SayWhat& operator[](std::string_view s) {

and you compile with --std=c++17 -Wall , you get:你用--std=c++17 -Wall编译,你得到:

<source>: In function 'int main()':
<source>:16:11: warning: null argument where non-null required (argument 2) [-Wnonnull]
   16 |     ohNo[0]; // you didn't! this compiles.
      |           ^

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM