I apologize for the title, but I wasn't able to think of anything better considering how peculiar this issue is.
I found this issue inside over a thousand-line long header, and eventually was able to narrow it down to the following code sample:
#include <vector>
#include <algorithm>
#include <type_traits>
namespace cpplinq
{
template <typename T>
struct IEnumerable : private std::vector<T>
{
template <typename ACont, typename U>
friend IEnumerable<U> LINQ(ACont);
};
template <typename ACont, typename T = typename std::decay<decltype(*(std::declval<typename std::remove_reference<ACont>::type>().begin()))>::type>
IEnumerable<T> LINQ(ACont cont)
{
IEnumerable<T> ret;
std::for_each(cont.begin(), cont.end(), [&ret](T val) {ret.push_back(val); });
return ret;
}
}
int main()
{
auto mvalue = cpplinq::LINQ(std::vector<int>{1,2}); // compiles
auto mmvalue = cpplinq::LINQ(std::vector<int>{1,2}); // doesn't compile
auto mmmvalue = cpplinq::LINQ(std::vector<int>{1,2}); // doesn't compile
return 0;
}
Please ignore the function and struct names as they aren't relevant here.
When compiled with clang 3.4.1 or 4.0.0, the "mvalue" line compiles, and the two following lines don't, despite being syntactically identical.
https://godbolt.org/g/qaJzyQ - The version with only the "mvalue" line.
https://godbolt.org/g/T9DZek - The version with all three.
What's even more peculiar is that if you remove the "namespace cpplinq", and the associated braces, the entire thing compiles without a hitch, as shown here: https://godbolt.org/g/0Wl3jV