为什么“is_base_of”不能在类声明(不完全类型)中使用?

20
我完全理解为什么这是无法实现的:

class Base {};
class A;
static_assert(std::is_base_of<Base, A>::value, "");

由于没有关于“类层次结构”的信息,因此以下内容无法工作:
class Base {};
class A : public Base {
    static_assert(std::is_base_of<Base, A>::value, "");
};
(produce: an undefined class is not allowed as an argument to compiler intrinsic type trait)

在静态断言行上,按照该概念的定义,类型“A”仍未完成。然而,编译器已经了解了“类层次结构”,并可以提供答案。

当然,这个静态断言可以移动到析构函数或其他地方来解决此问题,但有些情况下无法这样做,例如:

class Base {};

template<typename T>
struct type_of {
    static_assert(std::is_base_of<Base, T>::value, "T is not derived from Base");
    using type = int; //* Some normal type in real use
};

class A : public Base {
public:
    type_of<A>::type foo(); // Will not compile
};

它不应该被允许吗?


5
编译器已经知道“类层次结构”。是的,但std::is_base_of不是核心功能,而是一个功能,这使得事情变得更加复杂。 - DeiDei
2
@DeiDei:换句话说,“C++是一个接着一个的巨型抽象泄漏”? ;) - Lightness Races in Orbit
目前is_base_of是一个非常普通的类模板。为了实现您的想法,您需要一个特殊的魔法实现is_base_of和标准的特殊许可来执行此魔法。标准已经太大了。我们不希望为边际利益使其变得更大更复杂。 - n. m.
我们不想让它变得更加庞大和复杂。请你为自己说话 :p 没有什么大的魔法 - 只是逻辑 :) 如果一个特性不需要了解类的所有内容,只需要了解其“类层次结构”- 为什么要这样做.. - user2561762
1
欢迎您撰写一个建议书。 - n. m.
显示剩余4条评论
2个回答

19
在闭合大括号}后,类定义才算完成(也就是说,一个类被视为已定义)。在您的情况下,当您尝试使用Astd::is_base_of一起使用时,A还没有完全定义:
class A : public Base {
    // no closing brace for A yet, thus A isn't fully defined here
    static_assert(std::is_base_of<Base, A>::value, "");
};

另一方面,std::is_base_of 要求工作的类型必须是完全定义的。
因此出现了错误。
作为解决方法,你可以将断言放在 A 的析构函数中:
class A : public Base {
    ~A() {
        static_assert(std::is_base_of<Base, A>::value, "");
    }
};

事实上,类类型在其成员函数体中被认为是完全定义的。
更多细节请参见这里(强调是我的):

一个类在类说明符的}处被视为已完全定义的对象类型([basic.types])(或完整类型)。在类成员说明中,类在函数体、默认参数、noexcept-specifiers和默认成员初始化器(包括嵌套类中的这些内容)中被视为完整的。否则,在其自己的类成员说明中被视为不完整。


2
“在其成员函数体中定义完整后,类类型被认为是已完全定义的。” ……那是一个很好、深刻的观点! - Eljay
1
请点击这里 - 在类说明符的结尾处,一个类被认为是完全定义的对象类型([basic.types])(或完全类型)。在类成员说明中,在函数体、默认参数、noexcept-specifiers和默认成员初始化器(包括嵌套类中的这些内容)中,类被视为完整的。否则,它在自己的类成员说明中被视为不完整。 - skypjack

7

std::is_base_of的文档页面显示:

如果Base和Derived都是非联合类类型,并且它们不是相同的类型(忽略cv限定符),则Derived必须是完整类型;否则行为未定义。

一个类的定义在使用}关闭其作用域后才算完成。因此,在您的情况下,会生成错误。


请注意,静态断言可以出现在块级别命名空间/文件范围内。因此,您可以将其移动到类的外部,或者如果您不想将其放在头文件中,则移到实现文件中。


3
问题在问为什么会这样,不仅仅是确认它是这样。 - Lightness Races in Orbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接