Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 917
lrleon Avatar asked Dec 22 '15 19:12

lrleon


1 Answers

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.

like image 90
Columbo Avatar answered Nov 05 '22 16:11

Columbo