Search code examples
c++templatesc++11c++14name-lookup

error determining a generic return type in C++11


In the context of a C++14 application, I use a scheme which could be resumed as follows (minimal reproducible test):

template <class Container>
struct LocateFunctions {    
  auto get_it() const // <-- here is the problem
  {
    auto ret = typename Container::Iterator();
    return ret;
  }
};

template <typename T>
struct A : public LocateFunctions<A<T>> {    
  struct Iterator {};
};

int main() {  
  A<int> a;
}

This approach compiles and runs perfectly in C++14, with GCC and Clang compilers.

Now I want migrate my application to Windows and for that I'm using MinGW. Unfortunately, its latest version brings GCC 4.9 which does not compile C++14. That does not seem like a serious problem because I can rewrite the C++14 constructs in C++11. So, I rewrite the get_it() method as follows:

typename Container::Iterator get_it() const
{ 
  auto ret = typename Container::Iterator();
  return ret;
}

Unfortunately it does no compile. Both compilers produce the following error:

error: no type named ‘Iterator’ in ‘struct A<int>’
   typename Container::Iterator get_it() const
                            ^

I also tried:

auto get_it() const -> decltype(typename Container::Iterator())
{ 
  auto ret = typename Container::Iterator();
  return ret;
}

but I get exactly the same error.

Since two compilers fail to recognize the type of return, I suppose it is impossible to determine it. But I do not really know why.

Could someone please explain me why not compile and eventually a way for refactoring in C++11 that compiles?


Solution

  • You're using CRTP; LocateFunctions is instantiated with an incomplete specialization of A (A<int>), hence accessing that specialization's members gives the rather misleading error message ("no … named … in …" instead of "… is incomplete"). However, in your example the function temploid get_it is only (if ever) instantiated after A<int> is indeed defined, making the typename-specifier well-formed.

    As for a workaround, try to achieve a similar effect, e.g. via

    template <typename T=Container>
    typename T::Iterator get_it() const
    {
        static_assert(std::is_same<T, Container>{}, "You ain't supposed to supply T!");
        auto ret = typename T::Iterator();
        return ret;
    }
    

    Demo with GCC 4.9.