文字类型在运行时的惊人行为

7

我对使用clang 3.9编译的此代码的行为感到有些困惑:

struct A {
    constexpr A() = default;
    A(const A&) = delete;
    constexpr A(A&&) {}
    A& operator =(const A&) = delete;
    constexpr A& operator =(A&&) { return *this; }
    constexpr operator bool() const { return &self == this; }
private:
    A& self{*this};
};

constexpr A fooA() { return {}; }    

int main(int argc, const char * argv[]) {
    static_assert(fooA(), "");
    return fooA();
}

Godbolt链接:https://godbolt.org/g/CDFXAc 对于fooA,静态/编译时评估已正确进行;然而在运行时,构造函数似乎被完全省略了。 static_assert没有触发(如预期的那样),但是main函数仍然返回0。这是因为A是字面类型还是编译器bug呢?
如果是前者,请提供任何与标准相关的参考资料。

1
Godbolt 上的 GCC 6.3 失败了这个断言。 - AnT stands with Russia
@AnT 是的,7.0也可以,但这证明了什么? - Oleg Bogdanov
@ildjarn,您想详细说明哪部分是未定义行为吗? - Oleg Bogdanov
1
只是一个Clang的错误。 - Oliv
2个回答

2

这里是一个更为简化的例子:

struct A {
    constexpr A() : self(this) { }
    A* self;
};

int main() {
    constexpr A a{};
}

由于在初始化程序中使用this,因此无论是gcc还是clang都不接受此代码。但是,在constexpr构造函数中,只要它在一个常量表达式中,this是允许的,根据N3652。MSVC正确处理了这个问题。


在你的情况下,编译器不喜欢这段代码,而在我的问题中,编译器确实喜欢它,只是我不喜欢它产生的结果 :) - Oleg Bogdanov
如果您的代码无法编译,但 OP 的代码可以编译,则这不是 OP 问题的简化示例。 - M.M
1
@M.M 这是同样的问题 - 两个编译器都在处理 this 时出现了错误,它们恰好做了不同的错误事情,并且是不一致的错误事情,但这是同一个根本问题。 - Barry
此外,gcc无法编译OP的代码。Clang可以编译它,但会执行矛盾的操作。 - Barry
如果你在原始代码链接中直接返回A{},你会得到预期的行为,问题出在fooA上。 - dlavila

-2
问题在于输出结果取决于是否执行复制省略(如果没有,则将self初始化为临时值,因此fooA()变为非const并且其声明无效,然后您会得到意外的行为。在标准中,在这种情况下(对于c++14),没有强制要求复制省略,因此不同编译器会产生不同的行为。您可以在此报告中获取有关此问题的更多详细信息。

即使发生复制省略,拷贝构造函数仍然需要可用/可见,而在我的情况下它被删除了。 - Oleg Bogdanov
我指的是:当发生复制省略(直到C++17)在那些不保证复制省略的情况下,如果它发生了(自C++17以来),并且未调用复制/移动构造函数,则必须存在且可访问该构造函数(就像没有进行任何优化一样),否则程序将是非法的。 - Oleg Bogdanov
确切地说,这是自C++17以来的事情,如果不保证复制省略并且进行拷贝构造,则在C++14中不需要可见的拷贝构造函数。只需要看一下这个godbolt,你会发现在两种情况下都会调用构造函数,但是在第一种情况(fooA)中,self被初始化为一个临时变量,当bool()被调用时,A指向了另一个地址。 - dlavila
这句话也在我的Godblot链接中,被某人“为了清晰而删除”从问题中移除了… - Oleg Bogdanov
A是在fooA内部构建并通过值返回的,这听起来像是RVO的候选者。 - dlavila
显示剩余3条评论

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