如何检查 C++ 类是否不完整(仅声明)? [重复]

问题描述 投票:0回答:3

我想编写一个 C++ 函数来检查其模板参数类是否不完整,因此只有类声明可用,但没有所有类成员的完整定义。

我的功能

incomplete()
与一些演示程序一起看起来如下:

#include <type_traits>
#include <iostream>

template <typename T, typename V = void> constexpr bool is_incomplete = true;
template <typename T> constexpr bool is_incomplete<T, std::enable_if_t<sizeof(T)>> = false;
template <typename T> constexpr bool incomplete() { return is_incomplete<T>; }

struct A;
void print() { std::cout << incomplete<A>(); }
struct A {}; //this line affects GCC

int main()
{
    print();
}

它在 Clang 打印

1
中运行良好,但在 GCC 中,程序打印
0
,尽管
A
类在函数
print
中不完整。 https://gcc.godbolt.org/z/qWW3hqbEv

是 GCC 错误还是我的程序有问题?

c++ sfinae incomplete-type
3个回答
6
投票

哪个编译器是正确的目前尚未确定。这是CWG 第 1845 期

13.8.4.1 [temp.point]的当前措辞没有定义变量模板特化的实例化点。据推测,将第 1 段和第 8 段中对“类模板的静态数据成员”的引用替换为“变量模板”就足够了。

鉴于该问题仍处于起草阶段(且尚无规范性措辞,但为最新可用草案),因此仍未得到解决。目前尚不清楚变量模板是否可以具有多个实例化点(尽管问题报告者建议的方向很明确)。

对于具有多个实例化点的实体,翻译单元的末尾也是一个。此外,如果两点在模板的定义上存在分歧,那么这就是 ODR 违规,简单明了。 GCC 似乎提供了两点,所以你看到了结果。我倾向于同意 GCC 的观点。变量模板在很多方面都是类模板的静态数据成员的简写,并且静态数据成员确实具有多个实例化点。

无论哪种方式,这都是在玩鼻恶魔的风险。如果该状态可以在 TU 中更改(甚至可能在整个程序中更改),则实际上不可能可靠地检查类型的完整性。


1
投票

如果某个类在某个时刻变得完整,那么发现一个类是否不完整将违反一个定义规则(ODR),因此该问题应该没有有效的解决方案。

我建议的更多来自 https://eel.is/c++draft/temp.point 的引用:

1 对于函数模板特化...实例化点...紧跟在引用特化的命名空间范围声明或定义之后。

7 函数模板的特化...在翻译单元内可能有多个实例化点,除了上面描述的实例化点之外,

对于在...翻译单元...内具有实例化点的任何此类专业化,...[结束]翻译单元之后的点也被视为实例化点,

如果根据单定义规则,两个不同的实例化点赋予模板特化不同的含义,则该程序是格式错误的,无需诊断。


1
投票

已经正确指出,这可能会导致 ODR 违规,从而导致未定义的行为。为了使这个问题更加具体,请考虑下面的程序,其运行时行为取决于

static_assert
是否存在!显然这真的很恶心。

// is_incomplete is defined in the OP.

struct foo;

template <typename T>
void f() {
    if (is_incomplete<T>)
        puts("hello");
    else
        puts("goodbye");
}

int main() {
    f<foo>();
}

static_assert(is_incomplete<foo>);
struct foo{};

使用 GCC 13.1 和 Clang 16.0 编译时,程序显示

hello
。但是,如果删除
static_assert
,则输出为
goodbye
。 (参见此处。)

© www.soinside.com 2019 - 2024. All rights reserved.