这段看起来荒谬的代码在Clang和GCC都可以编译通过,这是一个bug吗?

8

今天我在尝试使用模板来让编译器从内部类中推断出外部类的类型,虽然最终没有找到解决方案(我猜可能是不可能实现的),但在尝试修复一个错误时,我遇到了非常奇怪的行为,我将其简化为以下代码片段。

struct A
{
    struct B{};

    template <typename T>
    struct EverythingIsFine
    {
        using Outer = T;
        using Inner = typename T::B::B::B::B::B::B;
    };

    using ItWillBeOkay = EverythingIsFine<B>; // Probably not ok
    using InnerProblem = ItWillBeOkay::Inner; // Still not ok
    using OuterProblem = decltype(B().ItWillBeOkay::Outer::B::B::B
                                     ::B::B::B::~B()); // Not even CLOSE to ok
};

意外的是,使用Clang和GCC编译器,版本分别为gcc version 5.3.1 20160121 (Debian 5.3.1-7)Debian clang version 3.6.2-3 (tags/RELEASE_362/final) (based on LLVM 3.6.2),编译选项为-std=c++11 -Wall -Wextra,没有任何警告或错误。
我发现它也可以在Ideone上使用C++14设置编译而不出问题。
然后,我使用了这个简单的测试来获取InnerProblemOuterProblem的确切类型:
template <class T> void Type();
int main()
{
    Type<A::InnerProblem>();
    Type<A::OuterProblem>();
}

两个编译器在编译测试时报告相同的类型:

在函数main中:
main.cpp:20: undefined reference to void Type<A::B>()
main.cpp:21: undefined reference to void Type<void>()

也就是说,InnerProblem的类型是A::B,而OuterProblem的类型是void


这是否符合标准,还是两个编译器都存在错误?
由于我看起来和我的编译器一样困惑,所以这段代码到底发生了什么?

编辑:为了简化后续步骤,因为我不明白为什么两个编译器不能给出相同的结果,下面的代码可以使用Clang编译,但无法使用GCC编译。

struct A
{
    struct B{};

    template <typename T>
    struct EverythingIsFine
    {
        using Inner = typename T::B::B::B;
    };

    using Problem = EverythingIsFine<B>::Inner::B::B::B; // Not ok
};

GCC现在输出以下错误:

main.cpp:11:26: 错误:'A::B::B'表示构造函数,而不是类型 using InnerProblem = EverythingIsFine::Inner::B::B::B; // 不正确


2
这段代码可以在ICC和MSVS上编译通过。 - NathanOliver
新问题 -> 请提出新的问题。如果上下文有帮助,请包含链接。 - Baum mit Augen
@BaummitAugen 我觉得我的新问题也是个重复的,所以这次我会弃权,但你是对的。 - tux3
1
这不是重复的问题。问题“为什么这段代码有效”和问题“为什么注入类名”是有区别的,前者的答案是“注入的类名”,而后者则是“为什么注入类名”。 - Pete Becker
@PeteBecker 那里的最佳答案包含了你提供的几乎相同的引用,所有必要的信息都在那里。这里不需要重复。 - Baum mit Augen
1个回答

6

它是有效的。

类名也插入到类本身的作用域中;这称为注入类名 (9/2)。

因此,B::B 命名为类 BB::B::B 也是如此,依此类推。

编辑:

因此,typename B::B 命名为类 Btypename B::B::B 也是如此,依此类推。


有道理,但如果我将 InnerProblem = ItWillBeOkay::Inner 替换为 InnerProblem = ItWillBeOkay::Inner::B::B::B;,现在 GCC 无法编译,而 Clang 仍然可以编译通过,这是定义良好的行为吗?GCC 表示 "'A::B::B' 是构造函数的名称,而不是类型"。 - tux3
@tux3 - 我不知道。我没有(也不会)跟随你问题中的所有嵌套typedef。 - Pete Becker
3
B::B 这里表示一个构造函数。只有极少数情况下它不是构造函数。也就是说,它并不合法,但 Clang 存在缺陷。 - Columbo
1
经过一些阅读,我的理解是这是Clang的一个错误,GCC正确地报告了它,所以我不能接受这个答案。 - tux3
1
你的更新同样是不正确的。typename-specifiers并不是忽略函数名的上下文。 - Columbo
显示剩余3条评论

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