Using typealias in place of typedef defined in class in definition

Viewed 65

Why does this fail to compile? I am trying to reduce the amount of repetition since in the real code the class name is huge and the type is actually a very long name

https://godbolt.org/z/8YarWs

#include <vector>

template<typename T>
struct S {
    using type = std::vector<T>;

    type f() const;
};

template<typename T>
using type_t = typename S<T>::type;

template <typename T>
type_t<T> S<T>::f() const { }

The code above fails as below

<source>:17:14: error: no declaration matches 'type_t<T> S<T>::f() const'
   17 | type_t < T > S < T >::f() const
      |              ^~~~~~~
<source>:8:10: note: candidate is: 'S<T>::type S<T>::f() const'
    8 |     type f() const
      |          ^
<source>:4:8: note: 'struct S<T>' defined here
    4 | struct S
      |        ^
2 Answers

This is a GCC bug; the closest(1) confirmed open bug report seems to be:

  • Bug 69348: alias declarations can not be used inside qualifiers of declarators

which highlights that GCC rejects the following well-formed program:

template <class T>
struct X {
    int foo();
};

template <class T>
using foo2 = X<T>;

template <class T>
int foo2<T>::foo()
{
}

with the error message

error: invalid use of incomplete type 'struct X<T>'

(1) Scanning GCC:s bugzilla, there seems to be numerous "rejects valid" (open/unconfirmed) bug reports relating to alias templates, where in most reports clang is reported to accept the (arguably well-formed) example programs.

I don't remember the exact reason why, but I know it has something to do with the sequence in which the compiler resolves the type names and symbols...
You could get it to work by changing a bit the structure of the declaration.

Since I don't know how much of this is possible for you, I included some variants.

https://godbolt.org/z/bGPGhW

#include <vector>
#include <iostream>

template <typename T>
struct S
{
    using type = std::vector<T>;

    auto f() const -> decltype(type());
    auto g() const -> decltype(type()); 
    decltype(type()) h() const;
};

template <typename T>
using type_t = typename S<T>::type;

template <typename T>
auto S<T>::f() const -> decltype(type())
{
    std::cout << "foo" << std::endl;
    type_t<T> t;
    return t;
}

template <typename T>
using other_type_t = decltype(typename S<T>::type());

template <typename T>
auto S<T>::g() const -> other_type_t<T>
{
    std::cout << "bar" << std::endl;
    other_type_t<T> t;
    return t;
}

template <typename T>
using last_type_t = decltype(typename S<T>::type());

template <typename T>
last_type_t<T> S<T>::h() const
{
    std::cout << "foobar" << std::endl;
    last_type_t<T> t;
    return t;
}

int main(){
    S<int> s;
    auto t = s.f();
    auto r = s.g();
    auto l = s.h();
    return 0;
}

Output:

Program returned: 0
Program stdout

foo
bar
foobar
Related