无法重载元组的模板运算符,但自定义类型正常

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

本文关键字:自定义 类型 运算符 重载 元组      更新时间:2023-10-16

我有以下代码的和平:

// 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>声明之间建立联系,所以我尝试使用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;
    }
}

我不知道为什么首先允许在第三方命名空间中声明运算符。