jeudi 4 mai 2017

Clang unable to find a friend function template of a templated class more than once if it's inside a namespace

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.

http://ift.tt/2pdZwb0 - The version with only the "mvalue" line.
http://ift.tt/2pdM8DB - 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: http://ift.tt/2qJzcWo

Aucun commentaire:

Enregistrer un commentaire