防止来自布尔值的隐式对象构造

Prevent implict object construction from bool?

本文关键字:对象 布尔值      更新时间:2023-10-16

我有一个类,X,它有以下构造函数:

class X{
    X(int64_t, int16_t, bool, int8_t);
    X(int64_t, int16_t, bool);
    X(double);
    X(double, FT);
    explicit X(const string&); 
    X(const string&, Y);
};

问题是编译器曾经创建了一个只传递布尔值的 X 对象(假设是双构造函数允许这样做?(并且它导致了问题。

为了防止这种情况,我显式地将 bool 构造函数删除了它:

explicit X(bool) = delete;

但现在我收到编译器错误:

EXPECT_EQUALS(X("9.8901099"), a/b);
EXPECT_EQUALS(X{"56267"}, x);
X x{"-56267E-1"};
X b("5");

编译器说我已经使用了已删除的函数explicit X(bool) = delete

如何防止从布尔值创建 X 的对象?

发生这种情况

是因为boolstd::string更适合const char[]。当您没有接受bool的 ctor 时,则选择std::string。当您有一个带bool的 ctor 时,将选择此重载。只有它被删除,所以调用它是非法的。

让我们看看一个简单的免费重载函数的这种行为:

auto foo(int) { cout << "foo int" << endl; }
auto foo(std::string const &) { cout << "foo string" << endl; }
auto main() -> int {
  foo(3);       // int
  foo("Asdf");  // std::string
}

添加bool重载时:

auto foo(int) { cout << "foo int" << endl; }
auto foo(std::string const &) { cout << "foo string" << endl; }
auto foo(bool) { cout << "foo bool" << endl; }
auto main() -> int {
  foo(3);      // int
  foo("Asdf"); // bool (also warning implicit conversion turn string literal into bool)
}

解决方案是添加char const *重载:

auto foo(int) { cout << "foo int" << endl; }
auto foo(std::string const &) { cout << "foo string" << endl; }
auto foo(char const *s) {
   cout << "foo char const *" << endl;
   // possibly:
   return foo(std::string{s});
}
auto foo(bool) = delete;
auto main() -> int {
  foo(3);      // int
  foo("Asdf"); // char const *
  foo(true);   // error
}

由于某些旧代码使用并返回char *而不是char const *因此您可能还需要添加char *重载。

首先,您需要了解显式删除 bool 构造函数会发生什么。它在重载选择中变得可用,当它是最佳匹配时,它会被使用,然后给出编译器错误,因为它被删除了。

这与你不定义它时不同,因为你确实可以通过隐式转换加后跟类X的(不同的(构造函数来"阻止"布尔值的转换,因为它是布尔值的最佳匹配,所以隐式转换停止到此。

考虑类的用法

X x("abc");

如果没有已删除的 bool 构造函数,这将选择 X(const string&)

但是,对于已删除的bool构造函数,这将选择explicit X(bool),该也会被删除,因此会给出编译器错误。正如 Kerrek SB 所说,解决这个问题的解决方案是也为 const char * 定义一个构造函数。

实际上,如果您不删除bool构造函数,则以下内容将选择double

X x(true);

通过首先转换为int,然后转换为double

因此,您可以选择删除或不删除的解决方案,但您需要了解后果和要求。如果删除它,则需要提供一个const char *构造函数。如果你不删除它,那么你需要接受这样一个事实,即bool可以用来构造你的类的实例。