c++language-lawyertrailing-return-typedependent-namededuction-guide

Dependent names in C++ deduction guides and trailing return types - bug in GCC or ill-formed?


A question to C++ standard lawyers. My feeling is that the following code should be 100% accepted, but GCC (all versions supporting C++23 including trunk) rejects constructs using trailing return type with dependent names:

#include <utility>

// wraps lvalues by ref and xvalues by move
template <typename T, auto size_>
struct wrap {
    T obj;
};

// error in gcc, ok in clang:
template <typename T>
wrap(T && a) -> wrap<T, size(a)>;

// error in gcc, ok in clang:
template <typename T>
auto make_wrap(T && a) -> wrap<T, size(a)> {
    return {std::forward<T>(a)};
}

// ok in gcc and clang but requires T to be
// default-constructible:
template <typename T>
auto make_wrap1(T && a) -> wrap<T, size(T{})> {
    return {std::forward<T>(a)};
}

// ok in gcc and clang:
template <typename T>
auto make_wrap2(T && a) {
    return wrap<T, size(a)>{std::forward<T>(a)};
}

Try it here: https://godbolt.org/z/EdbbWEYoj. The error is:

there are no arguments to 'size' that depend on a template parameter, so a declaration of 'size' must be available [-fpermissive]

Note that make_wrap1 works and the deduction guide would work too in GCC when using size(T{}) instead of size(a). But a clearly depends on the template parameter T so both should work and Clang accepts everything.

Is it then a bug in GCC?

(For those who want to instantiate any of the above definitions: note that the type template parameter T needs a definition of a constexpr callable size that can be called with an argument of type T)


Solution

  • It's just a confusing error message.

    This gcc bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104255 prevents using function parameters outside a function body even though it's well formed. So, -> wrap<T, size(a)> is actually -> wrap<T, size(<<<ERROR>>>)>, and apparantly gcc doesn't consider errors to be dependent.

    Here's the message for the actual problem further down:

    <source>:15:31: error: use of parameter outside function body before ')' token
       15 | wrap(T && a) -> wrap<T, size(a)>;
          |                               ^