C++ - 什么是"类型透明类"?

rph*_*phv 45 c++ gcc

使用gcc编译包含对十进制数据类型的支持的程序,我最近遇到以下错误:

error: type transparent class 'std::decimal::decimal32' has base classes

快速浏览GCC的源代码树,可以看到此错误消息gcc/cp/class.c.

什么是"类型透明类"?为什么这样的类有"基类"是错误的?

rod*_*igo 21

更多地阅读GCC的源代码,在semantics.c:

  if (TREE_CODE (t) == RECORD_TYPE
      && !processing_template_decl)
    {
      tree ns = TYPE_CONTEXT (t);
      if (ns && TREE_CODE (ns) == NAMESPACE_DECL
          && DECL_CONTEXT (ns) == std_node
          && DECL_NAME (ns)
          && !strcmp (IDENTIFIER_POINTER (DECL_NAME (ns)), "decimal"))
        {
          const char *n = TYPE_NAME_STRING (t);
          if ((strcmp (n, "decimal32") == 0)
              || (strcmp (n, "decimal64") == 0)
              || (strcmp (n, "decimal128") == 0))
            TYPE_TRANSPARENT_AGGR (t) = 1;
        }
    }
Run Code Online (Sandbox Code Playgroud)

此代码表示在以下情况下将类型标记为透明:

  • 它是一个结构,但不是模板;
  • 它位于命名空间级别,该命名空间是std::decimal.
  • 它被命名decimal32,decimal64decimal128.

class.c您遇到的错误检查,还有一些.

并在mangle.c:

      /* According to the C++ ABI, some library classes are passed the
         same as the scalar type of their single member and use the same
         mangling.  */
      if (TREE_CODE (type) == RECORD_TYPE && TYPE_TRANSPARENT_AGGR (type))
        type = TREE_TYPE (first_field (type));
Run Code Online (Sandbox Code Playgroud)

评论是关键.我认为这意味着透明类型被替换为其第一个(也是唯一的)成员的类型,因此它可以在第一个成员可以使用的任何地方使用.例如,在我include/decimal的类中std::decimal::decimal32有一个类型的字段__decfloat32(来自前一个typedef float __decfloat32 __attribute__((mode(SD)));),所以任何采用a的函数都__decfloat32可以采用a std::decimal::decimal32,反之亦然.甚至功能装饰都是一样的.这个想法可能是为了使这个类ABI与C类型兼容_Decimal32,_Decimal64并且_Decimal128.

现在,你是如何获得class decimal32基类的?我唯一的猜测是你包含了不兼容(可能更旧)的头文件,实现完全不同.

UPDATE

经过一番调查,看起来我对ABI和功能装饰的猜测是正确的.以下代码:

#include <decimal/decimal>
using namespace std::decimal;

 //This is a synonym of C99 _Decimal32, but that is not directly available in C++
typedef float Decimal32 __attribute__((mode(SD)));

void foo(decimal32 a) {}
void foo(Decimal32 a) {}
Run Code Online (Sandbox Code Playgroud)

给出了一个奇怪的错误:

/tmp/ccr61gna.s: Assembler messages:
/tmp/ccr61gna.s:1291: Error: symbol `_Z3fooDf' is already defined
Run Code Online (Sandbox Code Playgroud)

也就是说,编译器前端在重载中没有问题并发出asm代码,但由于两个函数都装饰相同,因此汇编程序失败.

现在,这是不是GCC的一致性,正如Ben Voigt在评论中所说的那样?我不知道......您应该能够使用您想要的任何两种不同类型编写重载函数.但是OTOH,如果Decimal32不使用某些编译器扩展就不可能获得类型,所以这种类型的含义是实现定义的......


Mac*_*ade 11

正如我在其中一篇评论中所提到的,类型透明类是某种基本类型的包装类,如整数等.

它们被称为透明,因为它们使用了运算符重载,这使它们就像它们包装的原始类型一样.

IE,要int在类中透明地包装,你需要重载=操作符,++操作符等...

显然,GNU的libstdc ++将这些类用于某些类型.不知道为什么......

关于基类问题,虽然我不是100%肯定,但这是一个猜测.

在C++中处理继承时,您通常需要声明方法,以解决向上转换的问题.

将方法声明为虚拟将告诉编译器为方法创建虚拟表,因此可以在运行时查看它们.
这当然会增加类的实例大小.

对于类型透明类,这是不可接受的,因为编译器将无法将这样的类的实例放在寄存器中(即,当传递参数等时),与包装类型不同,因此类赢了再也不透明了.

编辑

我不知道如何在GCC中声明这样一个透明类.我能想到的最接近的是透明工会:

http://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html

就像是:

class IntWrapper
{
    int _x;

    /* Constructor, operator overloads... */
};

typedef union
{
    int        integerValue;
    IntWrapper integerWrapper;
}
IntUnion __attribute__( ( __transparent_union__ ) );
Run Code Online (Sandbox Code Playgroud)

我的GCC版本似乎不支持它,但根据文档(参见上面的链接),这将允许intIntWrapper使用相同的调用约定透明地传递给函数int.