0

In the following scenario, inside function(), on line ss << bb, I get an error:

binary '<<': no operator found which takes a right-hand operand of type 'CommonType' (or there is no acceptable conversion) error.

My ADL understanding is that it will look into the current namespace (that is AppNamespace::InnerNamespace) and since no operator << is found, it will look into the namespace of the arguments and as the CommonType is in global namespace I would expect that the operator << defined in the CommonTypes.h to be found. Obviously my understanding is wrong. Can anyone figure out how this should be working?

Main.cpp

namespace AppNamespace
{

    typedef std::vector<std::string> OtherType;

    std::ostream& operator << (std::ostream& os, const OtherType& ot)
    {
        for (auto& el : ot)
        {
            os << el;
        }
        return os;
    }

    namespace InnerNamespace
    {
        void function()
        {
            CommonType bb;
            std::stringstream ss;
            ss << bb;
        }
    }
}


int main()
{
    AppNamespace::InnerNamespace::function();
    return 0;
}

CommonTypes.h:

    #pragma once

#include <vector>

typedef std::vector<uint8_t> CommonType;

std::ostream& operator << (std::ostream& os, const CommonType& bb)
{
        for (auto& el : bb)
        {
            os << el;
        }
        return os;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
Iancu
  • 1
  • Even without the alias problem, your understanding of [ADL](http://en.cppreference.com/w/cpp/language/adl) is wrong. overloads of the namespace of argument are considered even is one exists at global namespace. We first build the set of namespace to consider. – Jarod42 May 09 '18 at 16:54

3 Answers3

1

The problem here is that ADL lookup will be performed in std namespace and namespace where CommonType is declared is not relevant, so in order to make this work you need to place operator << in appropriate namespace:

namespace std
{

ostream& operator << (ostream& os, const CommonType& bb)
{
    for (auto& el : bb)
    {
        os << el;
    }
    return os;
}

}
user7860670
  • 35,849
  • 4
  • 58
  • 84
  • 2
    Isn't adding to namespace std UB? http://en.cppreference.com/w/cpp/language/extending_std – Paul Floyd May 09 '18 at 16:03
  • @PaulFloyd Not necessary, sometimes it is a proper way of utilizing standard library, for example `std::is_error_code_enum` or `std::default_delete` specializations. – user7860670 May 09 '18 at 16:08
  • that is mentioned in the link, partial specializations are allowed, declarations and full specializations not allowed. – Paul Floyd May 09 '18 at 16:10
  • @PaulFloyd That article is somewhat incomplete. `std::is_error_code_enum` is an example of allowed full specialization. In case of `operator <<` we can not place it anywhere else because ADL lookup will be performed in `std` namespace since both arguments are from `std` namespace. see [similar question - Where should I define operator >> for my specialization of std::pair](https://stackoverflow.com/questions/6885063/where-should-i-define-operator-for-my-specialization-of-stdpair) – user7860670 May 09 '18 at 16:15
  • "Isn't adding to namespace std UB?" - Usually, yes. But the standard explicitly allows adding template specializations to namespace std (and, as far as I know, that's the only thing you are allowed to add). – Jesper Juhl May 09 '18 at 16:24
1

My ADL understanding is that it will look into the current namespace (that is AppNamespace::InnerNamespace) and since no << operator is found, it will look into the namespace of the arguments and as the CommonType is in global namespace I would expect that the << operator defined in the CommonTypes.h to be found.

The misunderstanding is that CommonType is a new type. It is not a new type. It is just an alias.

You can resolve the function lookup problem using various methods.

  1. Define both the operator<< functions in the global namespace.

    #include <string>
    #include <sstream>
    #include <iostream>
    #include <vector>
    #include <cstdint>
    
    // Put everything from CommonTypes.h here to simplify things.
    typedef std::vector<uint8_t> CommonType;
    
    std::ostream& operator<<(std::ostream& os, CommonType const& bb)
    {
       for (auto& el : bb)
       {
          os << el;
       }
       return os;
    }
    
    namespace AppNamespace
    {
        typedef std::vector<std::string> OtherType;
    }
    
    std::ostream& operator << (std::ostream& os, const AppNamespace::OtherType& ot)
    {
       for (auto& el : ot)
       {
          os << el;
       }
       return os;
    }
    
    namespace AppNamespace
    {
       namespace InnerNamespace
       {
          void function()
          {
             CommonType bb;
             std::stringstream ss;
             ss << bb;
          }
       }
    }
    
    
    int main()
    {
       AppNamespace::InnerNamespace::function();
       return 0;
    }
    
  2. Define both the operator<< functions in AppNamespace namespace.

    #include <iostream>
    #include <vector>
    #include <cstdint>
    
    // Put everything from CommonTypes.h here to simplify things.
    typedef std::vector<uint8_t> CommonType;
    
    namespace AppNamespace
    {
       std::ostream& operator<<(std::ostream& os, CommonType const& bb)
       {
          for (auto& el : bb)
          {
             os << el;
          }
          return os;
       }
    
       typedef std::vector<std::string> OtherType;
       std::ostream& operator << (std::ostream& os, const OtherType& ot)
       {
          for (auto& el : ot)
          {
             os << el;
          }
          return os;
       }
    
       namespace InnerNamespace
       {
          void function()
          {
             CommonType bb;
             std::stringstream ss;
             ss << bb;
          }
       }
    }
    
    
    int main()
    {
       AppNamespace::InnerNamespace::function();
       return 0;
    }
    
  3. Define std::ostream& operator<<(std::ostream& os, CommonType const& bb) in another namespace and explicitly bring that function into scope in AppNamespace.

    #include <string>
    #include <sstream>
    #include <iostream>
    #include <vector>
    #include <cstdint>
    
    // Put everything from CommonTypes.h here to simplify things.
    typedef std::vector<uint8_t> CommonType;
    
    namespace CommonNamespace
    {
       std::ostream& operator<<(std::ostream& os, CommonType const& bb)
       {
          for (auto& el : bb)
          {
             os << el;
          }
          return os;
       }
    }
    
    namespace AppNamespace
    {
       // Bring the operator<< functions in CommonNamespace into the scope of
       // this namespace.
       using CommonNamespace::operator<<;
    
       typedef std::vector<std::string> OtherType;
       std::ostream& operator << (std::ostream& os, const OtherType& ot)
       {
          for (auto& el : ot)
          {
             os << el;
          }
          return os;
       }
    
       namespace InnerNamespace
       {
          void function()
          {
             CommonType bb;
             std::stringstream ss;
             ss << bb;
          }
       }
    }
    
    
    int main()
    {
       AppNamespace::InnerNamespace::function();
       return 0;
    }
    
R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

If you don't want to move your operator in std namespace as recommended by VTT, you can specify that it lives in the global namespace:

(::operator <<)(ss, bb);
piarston
  • 1,685
  • 1
  • 13
  • 25