Holt
Holt

Reputation: 37626

Unable to overload templated operator for tuple but ok for custom type

I have the following peaces of code:

// A.hpp

#include <vector>

namespace M {

    struct A {

        template <typename T>
        A& operator>> (std::vector<T> &t) {
            for (int i = 0; i < 4; ++i) {
                t.push_back(T());
                *this >> t.back();
            }
            return *this;
        }

        template <typename T>
        A& operator>> (T &t) {
            long long int v = 0;
            t = static_cast<T>(v);
            return *this;
        }

    };

}
// B.hpp

#include "A.hpp"

#include <tuple>

namespace N {

    struct X { };

    M::A& operator>> (M::A &b, X &x);

    void f ();
}
// B.cpp
#include "B.hpp"

#include <tuple>
#include <vector>
#include <iostream>

namespace N {

    M::A& operator>> (M::A &a, std::tuple<int, double> &v) {
        return a;
    }

    struct Y { };

    M::A& operator>> (M::A &a, Y &y) {
        return a;
    }

    M::A& operator>> (M::A &a, X &x) {
        // std::vector<std::tuple<int, double>> v2;
        std::vector<Y> v2;
        a >> v2;
        return a;
    }

    void f () {
        M::A a;
        X x;
        a >> x;
    }

}

And to run the above I simply do:

N::f();

This is all about overloading the >> operator to be able to read vectors (this is simplified code, but it is complete enough to show the problem).

The above code will compile and work as expected, but if I uncommented the commented line in B.cpp and comment the line below:

std::vector<std::tuple<int, double>> v2;
// std::vector<Y> v2;

It does not compile because the overload of operator>> for std::tuple<int, double> is not found and it tries to call the templated method and of course fails to compile t = static_cast<T>(0).

I thought that maybe the compiler is not able to make the link between the two declaration of std::tuple<int, double> so I tried with a typedef but it changed nothing.

Why is it possible to overload the >> operator for a custom type (e.g. Y) but not for a standard std::tuple? Is there a way to overload the function for std::tuple?

Side-note: This is a quite complex MVCE but I am not able to reproduce the problem if I put everything in a single file... Feel free to edit if you come up with a smaller example.

Upvotes: 2

Views: 102

Answers (1)

Sergei Tachenov
Sergei Tachenov

Reputation: 24879

My knowledge of the standard is a bit fuzzy, so please correct me if I'm wrong in some of my assumptions.

It seems that the compiler looks for the operator in the namespaces to which the arguments belong (preferred) and in the enclosing namespaces.

M::A& operator>> (M::A &a, Y &y) {
    return a;
}

This operator is declared within the same namespace as the Y struct so it works.

M::A& operator>> (M::A &a, std::tuple<int, double> &v) {
    return a;
}

This one is declared in the namespace N whereas its arguments belong to namespaces M and std, which means that the compiler will look in those namespaces and in the enclosing (global) namespace. If you move it into one of those, it works. For example, in B.cpp:

namespace M {

    M::A& operator>> (M::A &a, std::tuple<int, double> &v) {
        std::cout << "special call T" << std::endl;
        return a;
    }

}

I have no idea why it is allowed to declare the operator in a third-party namespace, so to speak, in the first place.

Upvotes: 1

Related Questions