有没有办法避免无意中将非布尔参数传递给布尔参数

Is there a way to avoid inadvertently passing a non-boolean argument to a boolean parameter?

本文关键字:布尔 参数传递 参数 无意中 有没有      更新时间:2023-10-16

我今天遇到了一个C++类型安全问题,我想知道是否有一种好方法可以让编译器在编译时检测到这个问题。 请考虑以下示例代码:

class Bar
{
public:
   void Foo(bool arg1 = false, int arg2 = 10, int arg3 = 20)
   {
      [...]
   }
};
int main(int argc, char ** argv)
{
   int x = 40, y = 50;
   Bar b;
   b.Foo();            // correct usage
   b.Foo(true, x, y);  // correct usage
   b.Foo(x, y);        // problem:  compiles but won't do what the caller expects
}

如对 b.Foo() 的最终调用所示,问题在于很容易忘记提供第一个参数,在这种情况下,编译器无法捕获错误。

如果我能让编译器说"错误,非布尔值被提供给布尔参数"之类的话,那就太好了。 这将迫使开发人员检查代码,如果他真的想传入x作为布尔值,他必须传入(x!=0)。

这似乎是使用"显式"关键字的好地方,但 AFAICT 该关键字不适用于函数参数。

(我意识到可以通过不为参数提供默认值来避免此类问题,但默认值可能非常方便)

您可以提供已删除的重载:

class Bar
{
public:
   void Foo(bool arg1 = false, int arg2 = 10, int arg3 = 20)
   {
      [...]
   }
    template <typename T>
    void Foo(T&&, int = 10, int = 20) = delete;
};

由于模板方法将与非布尔参数完全匹配。

函数重载可以捕获它

public: void Foo(bool arg1 = false, int arg2 = 10, int arg3 = 20);
private: void Foo(int&, ...); // can't omit leading arguments, dummy!

或者在 C++11 及更高版本中,使用 = delete(不过,一些所谓的 C++11 编译器尚不支持此功能)

正如您所指出的explicit bool在这种情况下不起作用 - 但我们可以设法使Explicit<bool>工作,这看起来几乎完全相同。

首先,一些SFINAE特质魔法有助于提供一个类,该类具有仅适用于实际bool的构造函数:

#include <type_traits>
template<typename U>
struct Explicit
{
    bool value;
    template<typename T = U, typename = std::enable_if_t<std::is_same<std::decay_t<T>, U>::value>>
    Explicit(T&& value) : value(value) { }
    operator U() { return value; }
};

有了这个助手,您可以简单地说:

void Foo(Explicit<bool> arg1 = false, int arg2 = 10, int arg3 = 20) { }

查看成功和失败,并特别注意错误消息:

prog.cpp:22:11:错误:无法将"x"从"int"转换为"显式

您可以尝试将此技巧与模板一起使用:

template <class T>
void foo(T param);
template <>
void foo<bool>(bool param)
{
}

foo(true); // OK
foo(9); // Won't c̶o̶m̶p̶i̶l̶e̶  link