【问题标题】:typesafe typedef in C++C++ 中的类型安全类型定义
【发布时间】:2012-03-24 04:27:34
【问题描述】:

我想在我的 C++ 程序中使用 typedef 之类的东西来增强类型安全性。

举个例子,假设我有两个函数

void function1(unsigned idOfType1);
void function2(unsigned idOfType2);

然后我会错误地将 idOfType2 传递给 function1,反之亦然。在这种情况下,我希望编译器给我一个错误。我知道我可以将这些 un​​signed 包装在一个结构中,但是我必须提供一个字段名称并使用 . 来访问它们,这有点不方便。有什么好办法吗?

编辑:据我所知,typedef 不能用于此目的,因为它只是类型的简写,不会用于类型检查。

【问题讨论】:

  • 枚举在你的情况下工作得很好......
  • @AlexanderPavlov 需要详细说明,这对我来说并不明显......
  • @Paul:您可以编写类似enum Type1 { dummy0 = 1, dummy1 = 1<<1, dummy2 = 1<<2, ..., dummy31 = 1<<31}; 的内容,并假设unsigned 在您的实现中是32 位类型,那么结果是一个可以保存unsigned 的任何值的枚举。那么void function1(Type1 id) 不会接受Type2,因为枚举之间不能隐式转换。
  • @SteveJessop:是的,我的意思是类似的方法,但不是使用二的幂的值 - Paul 不会按位计算,是吗?
  • @SteveJessop 我明白了,感谢您的解释。不过,这感觉有点骇人听闻。和两个整数的转换如何工作?

标签: c++ types type-safety


【解决方案1】:

使用Boost strong typedef:

typedef 为现有类型创建别名。它不会创建可用于匹配函数或模板参数的新类型...

使用 BOOST_STRONG_TYPEDEF 解决了这个问题...

BOOST_STRONG_TYPEDEF 是一个宏,它生成一个名为“name”的类包装和其原始类型的实例,并提供适当的转换运算符,以使新类型可替代它包装的类型。

【讨论】:

  • 请注意,Boost strong typedef 允许在赋值中进行隐式转换。
【解决方案2】:

正如你所说,typedef 在这里帮不了你。我想不出更好的方法,但是如果您使用 struct/class 选项进行包装,您可以使用转换运算符来消除成员方法或函数调用。

例如:

struct WrappedType
{
    operator type()
    {
         return _value;
    }

    type _value;  
}

我并不是说这是 的方法,请注意 ;-)

【讨论】:

  • 你能告诉我如何使用类型运算符吗? Boost strong typedef 通常看起来是一个不错的选择,但在我的情况下可能不起作用,所以我对这个解决方案很感兴趣!
  • @Konrad 太棒了!感谢您的详细说明。如果我不能使用 boost strong typedef 那么我将使用这个解决方案。
  • @Konrad 我喜欢这个答案,因为它让我深入了解了我以前不知道的类型运算符,所以我会接受你。
  • 这是 C++ 中你通常不会遇到的有趣的事情之一 :-)
  • @Paul:在你的代码中应该是operator unsigned()。当字面上输入operator type() 不起作用时不要感到惊讶:D
【解决方案3】:

这是对一个老问题的迟到答复。但是在 C++ 方面有新的发展,为了完整起见,我添加了这个答案:

opaque_typedef 库是作者尝试通过一个库来提供 opaque typedef 的大部分价值,而无需等待 opaque typedef 成为语言特性。

这个库的作者 Kyle Markley 有一个 short brilliant speech at the cppcon 2015 介绍这个库。他的演讲幻灯片在github 上,库的源代码在sourceforge 上提供。该库是仅头文件,用 C++11 编写。 gcc和clang都可以,但是VS2015好像有问题。

库的使用很简单。以下代码取自文档。它创建了一个不透明的 int 类型定义。它与 int 具有相同的接口(可以添加、移位、递增、比较等),但参数和返回值是新创建的类型,而不是 int:

#include "opaque/numeric_typedef.hpp"

struct myint : opaque::numeric_typedef<int, myint> {
  using base = opaque::numeric_typedef<int, myint>;
  using base::base;
};

【讨论】:

    【解决方案4】:

    有一个名为enum class 的c++11 特性,它基本上是一个类型安全的枚举。也许他们可以在这里提供帮助。

    【讨论】:

    • enums 始终是类型安全的,例如enum x { a }; enum y { b }; y = a; 不会编译。-
    • @phresnel 我认为这只是对了一半,因为枚举将被隐式转换为整数,不是吗?
    • @Paul:让我们同意 0.75*true,因为相反的情况不正确。所以,看起来枚举是 75% 类型安全的,...
    • @phresnel 好吧,无论如何,我总是对 boolean 是二进制感到不舒服!
    【解决方案5】:

    foonathan's blog post from 2016 涵盖了各种方法,从简单案例的示例类开始:

    class meter
    {
    public:
        explicit meter(int val)
        : value_(val) {}
    
        explicit operator int() const noexcept
        {
            return value_;
        }
    
    private:
        int value_;
    };
    

    并以一个简短的模板库结尾,该库支持自定义类型安全类型的算术运算,允许您编写以下代码:

    struct meter
    : strong_typedef<meter, int>, addition<meter>
    {
        using strong_typedef::strong_typedef;
    };
    

    【讨论】:

      【解决方案6】:

      你可以在你的函数中检查类型,如果不匹配,你可以打印一个错误什么的。

      可以使用typeid来检测变量类型,如下:

      typeid(*variablename*).name()
      

      正如答案之一here 中所建议的,这取决于编译器,您必须使用试错法来找出适合您的值。

      【讨论】:

      • 但是我必须在 every 函数调用中这样做 + 它依赖于编译器?抱歉,我认为这不是一个好的选择...
      • 无论如何都不起作用 - function1 中的参数 idOfType1 的类型始终为 unsigned。调用者传递的内容已经被转换。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-29
      • 1970-01-01
      • 1970-01-01
      • 2012-03-07
      • 2014-04-10
      • 1970-01-01
      相关资源
      最近更新 更多