2

Description

I'm overriding << operator for std::ostream to ease object display in my code. I use differents namespaces where I define types of object to display.

That leads me to a compilation error. I have found a fix. It seems that overriding must be declare in a global scope but I really don't understand why. Can someone explain what cause the error ?

Error

main.cpp:22:38: error: no match for ‘operator<<’ (operand types are ‘std::basic_ostream’ and ‘std::vector’)
         std::cout <<"printVector = " << data << std::endl;
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~

Sample code (don't compile)

Here is a dummy exemple to show the error.

#include <iostream>
#include <vector>

inline std::ostream&
operator<<(std::ostream& strm, std::vector<uint8_t>& buffer)
{
    return strm << "display std::vector<uint8_t>";
}
    
namespace aNamespace {
    enum TestEnum { a, b, c };
    
    inline std::ostream&
    operator<<(std::ostream& strm, TestEnum& value)
    {
        return strm << "display TestEnum";
    }
  
    static void printVector () 
    {
        std::vector<uint8_t> data {1, 12, 56, 241, 128};
        std::cout <<"printVector = " << data << std::endl;
    }
}


int main()
{
    aNamespace::printVector();
    return 0;
}

Environnement

C++11; GCC; Linux

Sample code fix (edited)

Here is a fix version of code for those who are interresed.

#include <iostream>
#include <vector>

inline std::ostream&
operator<<(std::ostream& strm, std::vector<uint8_t>& buffer)
{
    return strm << "display std::vector<uint8_t>";
}

namespace aNamespace {
    using ::operator<<;

    enum class TestEnum { a, b, c };
    
    inline std::ostream&
    operator<<(std::ostream& strm, TestEnum value)
    {
        return strm << "display TestEnum";
    }

    static void printVector () 
    {
        std::vector<uint8_t> data {1, 12, 56, 241, 128};
        std::cout <<"printVector = " << data << std::endl;
    }
}

int main()
{
    aNamespace::printVector();
    return 0;
}
Pibmy
  • 57
  • 1
  • 7
  • Another casualty of ADL. – Eljay Apr 14 '21 at 11:35
  • Try adding `using ::operator<<;`. – Evg Apr 14 '21 at 11:36
  • This doesn't address the question, but those `inline`s don't accomplish anything. `inline` means that it's okay to have multiple definitions of the inline function in different translation units, and that you promise that they'll all be identical. Since there is only one translation unit here, there's no reason to have it. If you defined that operator in a header file you'd mark it inline. – Pete Becker Apr 14 '21 at 13:56

1 Answers1

7

ADL strikes again

For resolving operator overloads, C++ uses Argument Dependent Lookup. In short, it will search the namespace of either parameter (in this case namespace std) and the namespace of the call of the operator (aNamespace), going up the hierarchy if no operator overload is found.

The namespace std does not contain any matching overload, but it contains other non-matching overloads, so parent namespace is not searched.

Now, if namespace aNamespace does not contain any overload of operator <<, parent namespace (global) is searched and correct overload is found, like in your "Sample code fix" example.
However, if it contains any overload for operator <<, even a non-matching one, lookup will not consider the parent namespace and the correct overload cannot be found.

This is one of the reasons why some C++ gurus advocate against extensive use of namespaces, or at least to limit it to a single namespace per project (see for example this article by Titus Winters).


One possible fix is as you listed, but it may have problems itself, when compiler will look for overload using aNamespace::TestEnum. Prefer to have operator overload in the namespace which encapsulates one of the arguments.

Better solution would be to explicitly add operators from global scope to your namespace scope or function scope:

 using ::operator<<;
Yksisarvinen
  • 18,008
  • 2
  • 24
  • 52