Created attachment 33681 [details] Minimal working example based on std::vector<bool> When trying to convert some iterators in our numerics library DUNE (dune-project.org) to return temporaries instead of const refs, I stumbled over the following bug in GCC 4.8.3 and 4.9.1: Given a class with an operator*() that returns a temporary: struct iterator { ... proxy operator*() const { return ...; } proxy get() const { return ...; } }; the expression 'decltype(*it())' should yield 'proxy', and a variable declared with 'auto&& x = *it;' should be of type 'proxy&&'. The above is true inside normal functions: void bar(iterator it) { typedef decltype(*it()) P; // P == proxy auto&& x = *it; // decltype(x) == proxy&& } But inside a function template, 'decltype(*it())' becomes 'proxy&', and 'auto&& x = *it' fails with a compiler error: template<typename T> void foo(T t, iterator it) { typedef decltype(*it()) P; // P == proxy& auto&& x = *it; // compiler error, see below } error: invalid initialization of non-const reference of type 'proxy&' from an rvalue of type 'proxy' For some reason, the compiler deduces the wrong type early on and then fails when it later realizes the correct return type... This problem can easily be observed with a std::vector<bool>, which returns a proxy from its iterator. I have attached a minimal working example: The contained code (which creates a vector and iterates over its contents with a range-based for loop using auto&&) works in main() and in a normal function bar(), but it fails to compile in foo(), which is a function template. GCC 4.6 and 4.7 compile foo() correctly.
I forgot to mention: This problem is specific to operator*(). If you add an equivalent normal member function and replace the dereferencing operations with calls to that function, the problem does not occur.
Do you mean decltype(*it) (without the extra parentheses) everywhere?
Ooops, yes, I did. Actually I meant to write decltype(*iterator()), but decltype(*it()) doesn't make sense, of course. And to avoid further confusion: The function get() in the iterator is just there as an example for a function that avoids the problem (see comment #1).
Here is a complete example demonstrating the problem, which is also present in gcc 5.0.0 20141010 (experimental): //------------------------------- struct proxy {}; struct iterator { proxy operator*() { return proxy(); } }; //#define DEACTIVATE #ifndef DEACTIVATE template<typename T = int> #endif void foo(iterator it) { auto&& x = *it; } int main() { iterator it; foo(it); } //-------------------------------
The exact same problem is present on operator[] : //------------------------------- struct proxy {}; struct iterator { proxy operator*() { return proxy(); } proxy operator[](int i) { return proxy(); } }; //#define DEACTIVATE #ifndef DEACTIVATE template<typename T = int> #endif void foo(iterator it) { auto&& x = *it; auto&& y = it[1]; } int main() { iterator it; foo(it); } //-------------------------------
Clang accepts the code with or without DEACTIVATE defined.
This is fixed in mainline. I'm adding the reduced testcases and closing the bug.
Author: paolo Date: Tue Dec 15 10:18:13 2015 New Revision: 231646 URL: https://gcc.gnu.org/viewcvs?rev=231646&root=gcc&view=rev Log: 2015-12-15 Paolo Carlini <paolo.carlini@oracle.com> PR c++/63506 * g++.dg/cpp0x/pr63506-1.C: New. * g++.dg/cpp0x/pr63506-2.C: Likewise. Added: trunk/gcc/testsuite/g++.dg/cpp0x/pr63506-1.C trunk/gcc/testsuite/g++.dg/cpp0x/pr63506-2.C Modified: trunk/gcc/testsuite/ChangeLog
Done.