由decltype要求的不完整类型的派生基转换

Fra*_*nte 12 c++ inheritance decltype language-lawyer

我遇到了这段包含尾随返回类型和继承的代码片段。

以下最小示例可以使用g ++而不是clang进行编译

struct Base {};

int foo(Base&) {
    return 42;
}

struct Derived : public Base {
    auto bar() -> decltype(foo(*this)) {
        return foo(*this);
    }
};

int main()
{
    Derived derived;
    derived.bar();  

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我们更改auto bar() -> decltype(foo(*this))decltype(auto) bar()(c ++ 14扩展名),代码也会使用clang进行编译。链接到Godbolt https://godbolt.org/z/qf_k6X

谁能解释我

  • 有什么auto bar() -> decltype(return expression)不同decltype(auto) bar()
  • 为什么编译器之间的行为不同
  • 正确的实现是什么?

Pas*_* By 5

This is a gcc bug, the trailing return type isn't within a complete-class context [class.mem]

A complete-class context of a class is a

  • function body,
  • default argument,
  • noexcept-specifier ([except.spec]),
  • contract condition, or
  • default member initializer

We see that a complete class is needed for the derived to base conversion from [conv.ptr]

A prvalue of type “pointer to cv D”, where D is a complete class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class of D.

and [dcl.init.ref]

“cv1 T1” is reference-compatible with “cv2 T2” if a prvalue of type “pointer to cv2 T2” can be converted to the type “pointer to cv1 T1” via a standard conversion sequence. In all cases where the reference-compatible relationship of two types is used to establish the validity of a reference binding and the standard conversion sequence would be ill-formed, a program that necessitates such a binding is ill-formed.

On the other hand, a function body is within a complete-class context and thus the derived to base conversion is well-formed. The return type involving a placeholder type (decltype(auto)) is valid as long as it is already deduced before an expression using it.

For a possible workaround in C++11, you may use

auto bar() -> decltype(foo(std::declval<Base&>()))
{
    return foo(*this);
}
Run Code Online (Sandbox Code Playgroud)

provided you know that you want to call it with Base.