Search code examples
c++namespacesambiguous

Importing a single class into a namespace appears to require that there be no function name collisions between the namespaces


The following code (surprisingly?) does not compile with either g++ or clang++ due to ambiguous calls to BuildStream when instantiating bar::BuildStream with bar::Rectangle as an input.

#include <iostream>
#include <sstream>

namespace foo {

struct Rectangle { double height, width; };
std::ostream& operator<<(std::ostream& os, const Rectangle& a) { return os; }

inline void BuildStream(std::ostringstream& os) { }

template<typename T, typename... Args>
void BuildStream
(std::ostringstream& os, const T& item, const Args& ...args) {
  os << item;
  BuildStream(os, args...);
}

} // namespace foo

namespace bar {

inline void BuildStream(std::ostringstream& os) { }

template<typename T, typename... Args>
void BuildStream
(std::ostringstream& os, const T& item, const Args& ...args) {
  os << item;
  BuildStream(os, args...);
}

using Rectangle = foo::Rectangle;

} // namespace bar

int main(int argc, char* argv[]) {
  std::ostringstream os;
  bar::BuildStream(os, 1, 2);
  bar::BuildStream(os, bar::Rectangle(), bar::Rectangle());
  return 0;
}

The ambiguity would seem to imply that, generally speaking, if a class is imported from namespace foo into namespace bar, then care must be taken to ensure that no functions in bar have names equal to functions in foo. This clearly throws a wrench in some of the benefits of namespaces; is there a more nuanced approach to avoiding such ambiguities?

The context is foo being the namespace of a large library and bar being a consumer project's namespace (and this problem showed up in practice).


Solution

  • The issue here is with argument-dependent lookup. bar::Rectangle is actually a foo::Rectangle, so the call to BuildStream(os,args...) needs to check in foo's namespace as well for overloads.

    Care does need to be taken. You can avoid the ambiguity by qualifying the call:

      template<typename T, typename... Args>
      void BuildStream(std::ostringstream& os, const T& item, const Args& ...args)
      {
        os << item;
        bar::BuildStream(os, args...); // qualified
      }