【问题标题】:Disable implicit conversion between typedefs禁用 typedef 之间的隐式转换
【发布时间】:2015-09-29 15:31:16
【问题描述】:

在 C++11 中,是否有一种干净的方法来禁用 typedef 之间的隐式转换,或者您是否必须做一些讨厌的事情,例如将您的 int 包装在一个类中并定义和删除各种运算符?

typedef int Foo;
typedef int Bar;
Foo foo(1);
Bar bar(2);
bar = foo; // Implicit conversion!

【问题讨论】:

  • Typedef 不是真实类型,它们只是真实类型的简写或别名。
  • 所以foobar其实是同一个类型,不涉及转换。
  • 是的,这很清楚。但是现代 C++ 是否有一个替代品可以创建一个没有一堆样板的新类型?
  • @Barmar:我宁愿强调事物和事物的名称之间的区别。声明(包括 typedef 声明)引入 names,而不是 types。一个类型可以有多个名称。
  • @AndrewWagner 所以你基本上想要一些等同于 typedefs 的强类型枚举的东西?我不知道有一个。

标签: c++ types typedef


【解决方案1】:

HelloWorld 解释了为什么你所拥有的东西不能工作。您将需要通常称为“强大”的typedef 来做您想做的事。一个示例实现是BOOST_STRONG_TYPEDEF

#include <boost/serialization/strong_typedef.hpp>    

BOOST_STRONG_TYPEDEF(int, a)
void f(int x);  // (1) function to handle simple integers
void f(a x);    // (2) special function to handle integers of type a 
int main(){
    int x = 1;
    a y;
    y = x;      // other operations permitted as a is converted as necessary
    f(x);       // chooses (1)
    f(y);       // chooses (2)
}

如果我们已经完成了typedef int a;,那么代码将是模棱两可的。

【讨论】:

  • 也不错。 BOOST_STRONG_TYPEDEF 是否还引入了包装器,还是免费的?
  • @HelloWorld "BOOST_STRONG_TYPEDEF 是一个宏,它生成一个名为 "name" 的类,该类包装其原始类型的实例并提供适当的转换运算符,以使新类型可替代它包装了。”
  • @HelloWorld 它必须引入一个包装器,因为你在回答中给出的原因:)
  • 谢谢。我只是在等待一些魔法,我不知道提升吗:)
【解决方案2】:

C++ 标准说:

7.1.3 typedef 说明符

使用 typedef 说明符声明的名称成为 typedef-name。在其声明的范围内,typedef-name 在语法上等同于关键字,并命名与标识符关联的类型 第 8 条中描述的方式。 typedef-name 因此是另一种类型的同义词。 typedef-name 确实 不要像类声明 (9.1) 或枚举声明那样引入新类型

但是例如classstruct 引入新类型。在以下示例中,uniqueUnused 实际上什么都不做,只是用于创建不同的类型Value&lt;int, 1&gt; != Value&lt;int, 2&gt;。所以也许这就是你正在寻找的东西。 请记住,不能保证编译器会摆脱外部结构!唯一保证此代码为您提供与 int 相同的大小

template<typename T, int uniqueUnused>
struct Value
{
  Value() : _val({}) {}
  Value(T val) : _val(val) { }
  T _val;
  operator T&() { return _val; }

  // evaluate if you with or without refs for assignments
  operator T() { return _val; }
};

using Foo = Value<int, 1>;
using Bar = Value<int, 2>;
static_assert(sizeof(Foo) == sizeof(int), "int must be of same size");
static_assert(sizeof(Bar) == sizeof(int), "int must be of same size");

如果你想基于一个类创建一个新类型,你可以简单地使用这个例子(这不适用于标量类型,因为你不能从整数继承):

class Foo : public Bar // introduces a new type called Foo
{
    using Bar::Bar;
};

【讨论】:

  • 标准处理定义的别名是否使用相同的方式?
【解决方案3】:

我想做一些类似的事情来保持不同的索引不仅在逻辑上分开,而且由编译器强制执行。我想出的解决方案基本上是只定义一个元素的结构。在某些方面它使用起来更痛苦,但它非常适合我的情况,因为我通常不需要处理很多代码的索引的实际值,只需传递它即可。

typedef struct{uint16_t i;} ExpressionIndex;
typedef struct{uint16_t i;} StatementIndex;

现在,尝试做

ExpressionIndex foo() {
    StatementIndex i;
    return i;
}

产生错误error: incompatible types when returning type ‘StatementIndex’ but ‘ExpressionIndex’ was expected

类型之间的转换有点痛苦,但这是我改变的意图。

ExpressionIndex exp = (ExpressionIndex){stmt.i};

【讨论】:

    【解决方案4】:

    这不是严格的类型检查,但可以通过使用原始或 Apps Hungarian Notation (H.N.) 使非法转换可见。如果您认为 H.N. 意味着名称类型作为前缀,那您就错了(它是 System H.N.,嗯,是不必要的命名开销)。

    使用 (Apps) H. N.,变量前缀标记的不是类型(例如 int),而是用途,例如计数器、长度、秒等。因此,当您将计数器添加到包含经过时间的变量时,您编写cntSomethingCounter + secElapsedSinceLastSomething,您可以看到它有异味。编译器不会发出警报,但它会戳你的眼睛。

    阅读更多:http://www.joelonsoftware.com/articles/Wrong.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-29
      • 2023-03-17
      • 1970-01-01
      • 1970-01-01
      • 2015-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多