Namespace, template typedef and operator overloads – The Evil trio


What an unconfortable situation ?

I had the following code which would not compile.

// Range.h
#include <boost/numeric/interval.hpp>
#include <iostream>
namespace App {
    template <typename T> using Range = boost::numeric::interval<T>;
    template <typename T>
    std::ostream& operator<<(std::ostream &os, const Range<T> &r) { ... }
} // namespace App


// Filter.cpp
#include "Range.h"
#include <iostream>
namespace App {
namespace Core {
    void f()
    {
        Range<double> r { 1.0, 10.0 };
        std::cout << r << std::endl;
    }
} // namespace Core
} // namespace App

Compilation would fail on the line with the std::cout with a lengthy list of non-matching candidates. Nowhere in this list, the operator overload provided by Range.h would be listed.

Why ?

Because the compiler takes App::Range<T> for an alias of boost::numeric::interval<T>;

Because consequently the compiler looks for:

  • std::ostream& App::Core::operator<<(std::ostream &, const boost::numeric::interval<double> &)
  • std::ostream& boost::numeric::operator<<(std::ostream &, const boost::numeric::interval<double> &)
  • std::ostream& std::operator<<(std::ostream &, const boost::numeric::interval<double> &)

The namespace that contained the using is completely forgotten in the process.

Solution ?

// Filter.cpp
namespace App {
namespace Core {

    using App::operator<<;

    void f() { ... }
} // namespace Core
} // namespace App

That using clause can be placed at namespace level or in function level. In first case (as shown above), all the overloads of the operator that have been defined in App namespace become visible in App::Core namespace.

Share

Les commentaires sont fermés.