C++ 奇怪的 templates/namespaces 行为

C++ weird templates/namespaces behavior

我在编译代码时遇到问题,因为它无法在模板上找到匹配的函数。我已经将问题缩小到这个例子:

namespace cv
{
    class FileNode
    { };

    template<typename _Tp> static inline void operator >> (const FileNode& n, _Tp& value)
    {
        read(n, value, _Tp());
    }

    static inline void read(const FileNode& node, bool& value, bool default_value)
    { }
}

class K
{   };

namespace D
{
    class A
    { };
}

template<class X>
static void read(const cv::FileNode& node, X& x, const X& default_value)
{
    return;
}

using namespace D;
class B
{
    void read(const cv::FileNode& fn)
    {
        A a;
        fn >> a;
    }
};

int main(int argc, char* argv[]) { }

在 Gcc 9.10 上出现以下错误:

invalid initialization of reference of type 'bool&' from expression of type 'D::A'  { read(n, value, _Tp()); }

2019 年 Visual Studio:

Error   C2664    'void cv::read(const cv::FileNode &,bool &,bool)': cannot convert argument 2 from '_Tp' to 'bool &'

我发现以下任何更改都会使代码编译:

不幸的是,none 之前的修复适用于我原来的问题,但我仍然没有真正理解为什么它无法找到 read 模板。

ADL 反击:

template<typename _Tp> static inline void operator >> (const FileNode& n, _Tp& value)
{
    read(n, value, _Tp());
}

read 未声明,因此只能通过 ADL

找到

因此它将在与 FileNode 关联的命名空间中搜索(因此 cv),以及与 _Tp 关联的命名空间。

_TpD::A 时,它将是命名空间 D

read 唯一可能的重载是 cv::read,它需要 bool.

read<T> 的声明移至上方 cv::operator >> 解决问题,也将考虑使用 ADL。

Demo

通常,我会将 类 和命名空间分隔到不同的 .hpp 文件中,这将自动强制您创建函数:

template<typename _Tp> static inline void operator >> (const FileNode& n, _Tp& value)

在使用它的命名空间之前声明(如cv)。

我认为避免此类问题的最好方法是清理代码,并使其尽可能分离和独立。这样,无论何时你想在另一个命名空间中使用这个独特的 read 函数,你都不需要将新的命名空间放在这个文件中,或者包括当前在这个文件中的所有命名空间。