为什么成员函数 return 类型的实例化要比它所依赖的表达式类型晚得多?

Why is member function return type instantiated much later than the expression types it depends on?

请原谅这个令人困惑的标题。

我有这段代码,它被 GCC、Clang 和 MSVC 接受:

#include <type_traits>

template <typename T>
struct Reader
{
    friend auto adl(Reader<T>);
};

template <typename T, typename U>
struct Writer
{
    friend auto adl(Reader<T>) {return U{};}
};

struct A
{
    struct Tag {};
    auto helper() -> decltype(void(Writer<Tag,decltype(this)>{})) {}
    using Self = std::remove_pointer_t<decltype(adl(Reader<Tag>{}))>;
};

它使用有状态元编程来编写一个类型,然后再读回它。

注意 Writer 的奇怪用法。我想我可以将其简化为:

auto helper() -> Writer<Tag,decltype(this)> {return {};}

但随后它停止工作(在所有三个编译器上),因为 Writer 在读取发生后被实例化。 run on gcc.godbolt.org

但是,如果我随后将 using Self = ... 移动到 helper 下面的 static 函数 body,它将再次开始工作。意思是 Writer 现在与 helper 的 body 一起实例化,而不是与其声明一起实例化。

在尝试了不同的 return 类型之后,似乎 Writer 在用作 表达式 的类型时会提前实例化。这是另一个例子:auto helper() -> std::enable_if_t<(Writer<Tag,decltype(this)>{}, true), void> {}

造成这种差异的原因是什么?既然三个编译器都同意这一点,这不是有状态模板的侥幸,对吧?

声明 auto helper() -> Writer<Tag,decltype(this)>; 不会导致 Writer<Tag,decltype(this)> 的实例化,因为 return 类型不需要完整。

helper() 的定义以及 Writer<Tag,decltype(this)> 的隐式实例化然后在 complete-class 上下文中,编译器似乎认为这意味着成员函数定义位于在 class 定义之后,并且 Writer<Tag,decltype(this)> 的 point-of-instantiation 也在 class 定义之后。

不过,我不知道这是否真的应该如何工作,或者标准中的哪一段会指定此行为。

仅在另一个(静态)成员函数的主体中读取标记会导致读取也位于完整的 class 上下文中,即在上述解释中的 class 定义之后,这就是它再次起作用的原因。