使用gcc编译包含对十进制数据类型的支持的程序,我最近遇到了以下错误:
error: type transparent class 'std::decimal::decimal32' has base classes
快速浏览一下GCC的源代码树,可以看到gcc/cp/class.c
中出现此错误消息。
什么是“类型透明类”?为什么这样的类有“基类”是错误的?
答案 0 :(得分:21)
在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
。decimal32
,decimal64
或decimal128
。在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代码,但由于两个函数都装饰相同,因此汇编程序失败。
现在,这是不是GCC的一致性,正如Ben Voigt在评论中所说的那样?我不知道......您应该能够使用您想要的任何两种不同类型编写重载函数。但是OTOH,如果不使用某些编译器扩展就不可能获得Decimal32
类型,所以这种类型的含义是实现定义的......
答案 1 :(得分: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__ ) );
我的GCC版本似乎不支持它,但根据文档(参见上面的链接),这将允许int
或IntWrapper
使用相同的调用约定透明地传递给函数int
。