防止采用 const std::string& 的函数接受 0
Prevent function taking const std::string& from accepting 0
千言万语:
#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;
}
编译器在将数字 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)
构造函数进入方法,这无缘无故地产生了同样的问题(google 上述错误)。
问题
有没有办法在 class 方面解决这个问题,让 API 用户感觉不到这一点,并且在编译时检测到错误?
也就是加一个重载
void operator[](size_t t) {
throw std::runtime_error("don't");
}
不是一个好的解决方案。
std::string(0)
之所以有效,是因为 0
是一个空指针常量。所以 0 匹配带指针的字符串构造函数。然后代码 运行 与不能将空指针传递给 std::string
.
的前提条件冲突
只有文字 0
会被解释为空指针常量,如果它是 int
中的 运行 时间值,则不会出现此问题(因为那时会重载resolution 将寻找 int
转换)。文字 1
也不是问题,因为 1
不是空指针常量。
因为这是一个编译时问题(文字无效值),您可以在编译时捕获它。添加这种形式的重载:
void operator[](std::nullptr_t) = delete;
std::nullptr_t
是nullptr
的类型。它将匹配 any 空指针常量,无论是 0
、0ULL
还是 nullptr
。并且由于删除了该函数,在重载解析时会导致编译时错误。
一个选项是声明 operator[]()
的 private
重载,它接受整数参数,但不要定义它。
此选项适用于所有 C++ 标准(1998 年起),不像 void operator[](std::nullptr_t) = delete
这样的选项从 C++11 开始有效。
将 operator[]()
设为 private
成员将在您的示例 ohNo[0]
上导致可诊断错误,除非该表达式由成员函数或 friend
class.
如果从成员函数或 class 的 friend
中使用该表达式,代码将编译但是 - 由于函数未定义 - 通常构建会失败(例如链接器由于未定义的函数而出错)。
使用 string_view 有帮助(有点)
从 C++17 开始,我们有 std::string_view
class。正是为了这个 use-case,将 non-owning references-to-string-like-objects 传递给只读取字符串的函数。您应该认真考虑将其用于此类运算符。
现在,std:: string_view
有其自身的设置问题(参见:enough string_view
to hang ourselves with),但在这里它会给您一个有用的警告。如果您更换:
SayWhat& operator[](const std::string& s) {
和
SayWhat& operator[](std::string_view s) {
你用--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.
| ^
千言万语:
#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;
}
编译器在将数字 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)
构造函数进入方法,这无缘无故地产生了同样的问题(google 上述错误)。
问题
有没有办法在 class 方面解决这个问题,让 API 用户感觉不到这一点,并且在编译时检测到错误?
也就是加一个重载
void operator[](size_t t) {
throw std::runtime_error("don't");
}
不是一个好的解决方案。
std::string(0)
之所以有效,是因为 0
是一个空指针常量。所以 0 匹配带指针的字符串构造函数。然后代码 运行 与不能将空指针传递给 std::string
.
只有文字 0
会被解释为空指针常量,如果它是 int
中的 运行 时间值,则不会出现此问题(因为那时会重载resolution 将寻找 int
转换)。文字 1
也不是问题,因为 1
不是空指针常量。
因为这是一个编译时问题(文字无效值),您可以在编译时捕获它。添加这种形式的重载:
void operator[](std::nullptr_t) = delete;
std::nullptr_t
是nullptr
的类型。它将匹配 any 空指针常量,无论是 0
、0ULL
还是 nullptr
。并且由于删除了该函数,在重载解析时会导致编译时错误。
一个选项是声明 operator[]()
的 private
重载,它接受整数参数,但不要定义它。
此选项适用于所有 C++ 标准(1998 年起),不像 void operator[](std::nullptr_t) = delete
这样的选项从 C++11 开始有效。
将 operator[]()
设为 private
成员将在您的示例 ohNo[0]
上导致可诊断错误,除非该表达式由成员函数或 friend
class.
如果从成员函数或 class 的 friend
中使用该表达式,代码将编译但是 - 由于函数未定义 - 通常构建会失败(例如链接器由于未定义的函数而出错)。
使用 string_view 有帮助(有点)
从 C++17 开始,我们有 std::string_view
class。正是为了这个 use-case,将 non-owning references-to-string-like-objects 传递给只读取字符串的函数。您应该认真考虑将其用于此类运算符。
现在,std:: string_view
有其自身的设置问题(参见:enough string_view
to hang ourselves with),但在这里它会给您一个有用的警告。如果您更换:
SayWhat& operator[](const std::string& s) {
和
SayWhat& operator[](std::string_view s) {
你用--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.
| ^