Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 734
Danra Avatar asked May 03 '21 13:05

Danra


1 Answers

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).

like image 159
songyuanyao Avatar answered Oct 30 '22 17:10

songyuanyao