无法为元组重载模板化运算符,但可以为自定义类型重载

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

我有以下代码:

// 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;
    }

}

而对于 运行 以上我只是做:

N::f();

这一切都是关于重载 >> 运算符以便能够读取向量(这是简化的代码,但它已足够完整以显示问题)。

以上代码将按预期编译和工作,但如果我取消注释 B.cpp 中的注释行并注释下面的行:

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

它没有编译,因为找不到 std::tuple<int, double>operator>> 重载,它尝试调用模板化方法,当然编译失败 t = static_cast<T>(0).

我认为编译器可能无法在 std::tuple<int, double> 的两个声明之间创建 link 所以我尝试使用 typedef 但它没有任何改变。

为什么可以为自定义类型(例如 Y)重载 >> 运算符,但不能为标准 std::tuple 重载?有没有办法为 std::tuple 重载函数?

旁注:这是一个相当复杂的 MVCE,但如果我将所有内容都放在一个文件中,我将无法重现该问题...如果你想出一个,请随意编辑较小的例子。

我对标准的了解有点模糊,所以如果我的某些假设有误,请纠正我。

编译器似乎在参数所属的命名空间(首选)和封闭的命名空间中查找运算符。

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

此运算符在与 Y 结构相同的命名空间中声明,因此它可以工作。

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

这个是在命名空间 N 中声明的,而它的参数属于命名空间 Mstd,这意味着编译器将在这些命名空间和封闭的 (全局)命名空间。如果你把它移到其中一个,它就会起作用。例如,在 B.cpp:

namespace M {

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

}

我不知道为什么允许在 third-party 命名空间中声明运算符,可以这么说,首先。