Danra
Danra

Reputation: 9906

Nested function template instantiation without forward declaration compiles on GCC but not on clang

The following doesn't compile in clang, but does in GCC (godbolt):

template <typename K, typename V>
std::ostream& operator<< (std::ostream& o, const std::map<K, V>& map)
{
    const char* sep = "{";
    for (const auto& x : map)
    {
        o << sep << "{" << x.first << ", " << x.second << "}";
        sep = ", ";
    }
    return o << "}";
}

template <typename T>
std::ostream& operator<< (std::ostream& o, const std::vector<T>& vec)
{
    const char* sep = "{";
    for (const auto& x : vec)
    {
        o << sep << x;
        sep = ", ";
    }
    return o << "}";
}

// Usage

int main()
{
    std::map<int, std::vector<int>> t = {{1, {2, 3}}, {4, {5}}};
    std::cout << t << std::endl;
    return 0;
}

Who is right?

btw, I know it's UB, but putting the two templates definitions in namespace std makes the code compile on clang as well.

Code borrowed from Is it possible to define operator<< for templated types?

Upvotes: 6

Views: 68

Answers (1)

songyuanyao
songyuanyao

Reputation: 172924

Clang is right. You should move operator<<'s declaration for std::vector before operator<<'s definition for std::map.

In unqualified name lookup,

(emphasis mine)

For a dependent name used in a template definition, the lookup is postponed until the template arguments are known, at which time ADL examines function declarations with external linkage (until C++11) that are visible from the template definition context as well as in the template instantiation context, while non-ADL lookup only examines function declarations with external linkage (until C++11) that are visible from the template definition context (in other words, adding a new function declaration after template definition does not make it visible except via ADL).

Upvotes: 3

Related Questions