【问题标题】:C++ - What is a "type transparent class"?C++ - 什么是“类型透明类”?
【发布时间】:2013-07-17 20:08:35
【问题描述】:

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

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

快速浏览一下 GCC 的源代码树会发现此错误消息位于 gcc/cp/class.c 中。

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

【问题讨论】:

  • 我正要讽刺“你查看了 gcc 的源代码,但懒得用谷歌搜索它?”但后来我用谷歌搜索了一下,什么也没找到。所以我给你道具,而不是蛇怪。 +1
  • 我能找到this,但我仍然不能说我知道什么是透明类。
  • “透明类型类”是一些基本类型(即 int)的类包装器,具有运算符重载,因此可以“透明地”使用包装类型。跨度>
  • @Macmade:你确定这就是 GCC 所指的透明类吗?为什么 GCC 会关心某个类是否恰好是某些程序员所说的透明包装器?不那么困惑。
  • 转载:ideone.com/nxDE55。是的,它必须是特定的命名空间和标识符。

标签: c++ gcc


【解决方案1】:

阅读更多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;
        }
    }

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

  • 它是一个结构体,而不是一个模板;
  • 它在命名空间级别,命名空间是std::decimal
  • 它被命名为decimal32decimal64decimal128

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));

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

现在,您如何获得带有基类的class decimal32?我唯一的猜测是您包含不兼容(可能较旧)的头文件,具有完全不同的实现。

更新

经过一番调查,看来我对 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) {}

给出奇怪的错误:

/tmp/ccr61gna.s: Assembler messages:
/tmp/ccr61gna.s:1291: Error: symbol `_Z3fooDf' is already defined

也就是说,编译器前端在重载中没有发现任何问题并发出 asm 代码,但是由于两个函数的修饰相同,因此汇编器失败了。

现在,这是否像 Ben Voigt 在 cmets 中所建议的那样不符合 GCC?我不知道......你应该能够用你想要的任何两种不同类型编写重载函数。但是OTOH,不使用一些编译器扩展是不可能得到Decimal32类型的,所以这种类型的含义是实现定义的......

【讨论】:

  • 这看起来像一个错误/不合格/无论如何。它违反了 17.6.5.11 中的规则,即“除非另有明确说明,否则具有不同名称的类型应为不同类型。”并且标准没有为std::decimal::decimal32 提供这样的权限(它没有提到我能找到的这个命名空间)
  • @BenVoigt:cmets 说它在“ISO/IEC TR 24733”中,free draft here。关于将这两种类型视为相同的权限,__decfloat32__attribute__((mode(SD)) 是编译器扩展和/或实现细节,因此与任何其他类型的可能交互对编译器的一致性没有影响。
  • 你不认为如果它进入std 命名空间,它必须符合std 的要求吗?命名空间std::decimal 绝对不是实现保留的名称。
  • 授权指定扩展,是的,但不遵循已批准标准规则的扩展是不符合标准的扩展。你会注意到我没有说“向命名空间添加东西std = 未定义的行为”。事实上,添加的东西违反了标准中规定的合同,这就是问题所在。
  • GCC 只有在至少有一个符合标准的程序显示问题时才符合标准。由于__attribute__((mode(SD)));,编写的代码不符合要求。 IOW,一个扩展可能看起来违反了标准的规则,只要它只在已经违反这些规则的程序中这样做。
【解决方案2】:

正如我的评论中提到的,type-transparent 类是一些原始类型(如整数等)的 wrapper 类。

它们被称为transparent,因为它们使用了运算符重载,这使得它们的行为就像它们包装的原始类型一样。

IE,要将int 透明地包装在一个类中,您需要重载= 运算符、++ 运算符等...

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

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

在处理 C++ 中的继承时,您通常需要声明 virtual 方法,以解决 upcasting 的问题。

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

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

编辑

我不知道如何在 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__ ) );

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

【讨论】:

  • 类型在哪里标记为“类型透明”?是否有某种 GCC 特定属性?我的意思是,你是说编译器认为某些东西是不可接受的,但它无法知道它是否应该被接受?
  • @sehe:没错。任何人都可以提供一个简短的可编译示例,没有任何 #include 行,导致 GCC 发出此诊断信息吗?如果没有,我认为我们不能说这个问题已经得到解答。
  • @Nemo 我刚刚评论了我的预感。这很可能是事情。但是我太累了,无法在gcc代码中查找关系。
  • @sehe:如果您点击 John Dibling 评论 (gcc.gnu.org/ml/gcc-patches/2011-08/msg02397.html) 中的链接,decimal32 似乎没有属性...而且我很确定没有记录在案的 GCC为此的属性。所以我认为这里发生了什么有点神秘。也许 GCC 内部专家会出现并澄清。
  • 无法使用我的 GCC 版本对其进行测试,但请参阅编辑以获取可能的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-15
  • 2023-03-26
  • 2017-01-26
  • 1970-01-01
  • 2012-11-17
  • 2014-08-26
  • 1970-01-01
相关资源
最近更新 更多