【问题标题】:Can I make the ternary operator treat my class like a bool?我可以让三元运算符像对待一本书一样对待我的课程吗?
【发布时间】:2010-08-20 08:57:15
【问题描述】:

我最近一直在进行大规模的重构,我更改了很多代码以返回布尔值,而不是显式的返回代码。为了帮助这种重构,我决定尽可能依靠编译器,让它告诉我需要更改代码的地方。我通过引入以下课程来做到这一点 (see here for the lowdown on how this works):

///
/// Typesafe boolean class
///
class TypesafeBool
{
private:
    bool m_bValue;
    struct Bool_ { 
        int m_nValue; 
    };
    typedef int Bool_::* bool_;
    inline bool_ True() const { return &Bool_::m_nValue; }
    inline bool_ False() const { return 0; }

public:
    TypesafeBool( const bool bValue ) : m_bValue( bValue ){}
    operator bool_() const { return m_bValue ? True() : False(); }
};

现在,我没有使用普通的 bool 类型作为返回类型,而是使用了这个类,这意味着我不能再编译这样的东西了:

TypesafeBool SomeFunction();
long result = SomeFunction(); // error

太棒了:它让编译器为我完成了很多艰苦的工作,从而使重构在庞大的代码库上变得易于管理。所以现在我已经完成了我的重构,我很想继续使用这个类并继续使用它,因为它为我们提供了内置 bool 类型所没有的额外安全级别。

然而,有一个“问题”阻止我这样做。目前我们在代码中大量使用了ternary operator,问题是它与这个没有显式转换的新类不兼容:

TypesafeBool result = ( 1 == 2 ? SomeFunction() : false ); // error: different types used
TypesafeBool result = ( 1 == 2 ? SomeFunction() : (TypesafeBool)false );

如果我可以“解决”这个问题,以便我可以无缝地使用我的课程,我可能会在整个代码库中继续使用它。有谁知道这个问题的解决方案?还是只是不可能做我想做的事?

【问题讨论】:

  • Ewww...C 风格的演员表...你的意思是 TypesafeBool(false) 在你的第二行吗?
  • @Dominic:这与我认为的相同(C++ 编译器将选择适当的构造函数)但我同意我更愿意看到对构造函数的显式调用。
  • @Naveen:尝试使用在侧面返回 TypesafeBool 的函数调用 :)
  • 这也在编译:TypesafeBool f() { return TypesafeBool(true); } int main() { TypesafeBool b = (1==1) ? f() : false; }
  • @Naveen: 你用的是哪个编译器?

标签: c++ type-conversion type-safety


【解决方案1】:

在条件运算符的上下文中,表达式的类型是最后两个操作数的普通类型。确定这种通用类型的完整规则有点复杂,但是您的情况恰好是微不足道的:如果两个可能的返回值之一是类类型,则另一个值必须具有相同的类,并且通用类型显然也是班级。

这意味着如果其中一个操作数是 TypesafeBool,那么另一个也必须是。

现在你真正想要解决的问题已经解决了。诀窍是不提供课程;而是使用 typedef。参见例如safe bool

【讨论】:

  • 我真正想要的是我可以为调试构建打开和关闭的东西:理想情况下,我会 #define 无论这个类型安全类是 bool 并且它可以与真正的 bool 互换实例,所以我可以在开发时获得这种额外的安全性。你能说明如何做到这一点吗?
  • @jkp:通常你会通过#ifdef NDEBUG typedef bool BOOL; #else typedef boost::safe_bool BOOL #endif来做到这一点。
【解决方案2】:
      class CCastableToBool
      {  
      public:
         // ...
         operator bool() const
         {  
           //...
           {
              return true;
           } 
           //...
           return false;
         }  
      private:
         // ...
      }; 

但请注意,在 C++ 中,拥有一个可以强制转换为 bool 的类被认为是非常危险的。警告你:-)

你可以在那里读到这个,SafeBool

您应该在所有三元测试中明确调用 TypesafeBool::True()。

TypesafeBool result = ( 1 == 2 ? SomeFunction().True() : false );

【讨论】:

  • @jkp 当你说它不起作用时,你的意思是它不能编译?你能粘贴编译错误吗?因为我已经编写了演员表操作符并且他们都工作了,所以我有点惊讶。
  • @jkp ;-) 没问题,但我坚持,我警告你,在我的脑海中,隐式转换为 bool 在 C++ 中是危险的。
  • @Stephane:很奇怪:如果您向该类添加带有参数的构造函数,它将无法编译......我一生都无法理解为什么:codepad.org/N1Eeohh4。如果您注释掉构造函数需要很长时间,它就可以工作。有什么想法吗?
  • @jkp 你能复制/粘贴编译错误吗?编译器使用构造函数进行动态转换。您可以尝试的一件事是显式声明 long 构造函数: class CCastableToBool { public: CCastableToBool() {} explicit CCastableToBool( long ) {} //...} 这样它就不会尝试使用它,除非您明确使用这样:CCastableToBool bTest(50);
  • @Stephane:对我来说这似乎是一种回归,jkp 有一个很好的安全布尔成语,而你的提议意味着回到一个不安全的成语。
【解决方案3】:

不知道无缝的方式,三元运算符的使用有一些限制……

但是,你为什么不定义两个常量呢?

TypesafeBool const True = TypesafeBool(true);
TypesafeBool const False = TypesafeBool(false);

然后:

TypesafeBool result = ( 1 == 2 ? SomeFunction() : False );

当然,这有点不正统,因为我使用大写来避免重复使用保留字:)

【讨论】:

  • 这个问题是与使用bool的代码互操作。我已经尝试过这样的事情,但发现这是绊脚石。
  • 您可以通过向 bool 添加隐式强制转换来进行互操作,但请注意,这种治疗方法可能比疾病更糟
  • @jk:隐式转换?你能举例说明你的意思吗?
  • 不支持隐式转换! operator bool 前段时间让我很伤心。
  • -1,声称的限制(精确类型匹配)显然是不真实的。两者之一甚至可能是一个 void throw 表达式!
【解决方案4】:

是否可以明确 TypesafeBool 的构造函数?当然,现在的用法必须是

TypesafeBool result( 1 == 2 ? b : false );

【讨论】:

  • @chubsdad:似乎是鸡和蛋。我需要明确的构造函数,以便编译器可以明确地计算出任何一方的类型应该解析为什么,但这反过来意味着我不能将bool 实例隐式分配给TypesafeBool 实例。很遗憾,我不认为直接替换是可能的。
【解决方案5】:

您能否使用一个将 bool 作为外部参数的赋值运算符,以及一个使用 TypesafeBool 的赋值运算符?这可能是要尝试的东西......

【讨论】:

    【解决方案6】:

    不错的尝试,但如果您的代码库很大,您最好使用静态检查器(例如 PC-Lint)来查找隐式 boolint 转换。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-10
      • 1970-01-01
      • 1970-01-01
      • 2018-04-17
      • 1970-01-01
      • 2020-03-02
      • 1970-01-01
      相关资源
      最近更新 更多