【问题标题】:C++ weird templates/namespaces behaviorC++ 奇怪的模板/命名空间行为
【发布时间】:2020-01-14 21:58:34
【问题描述】:

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

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()); }

在 Visual Studio 2019 上:

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

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

  • class A -> class A : public K
  • 删除readbool 的专门化
  • 删除cv命名空间
  • read 模板移入命名空间D

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

【问题讨论】:

  • 更改申报顺序可能会有所帮助:godbolt.org/z/ibe9DI
  • 有趣的是,该更改修复了 GCC 9.10 但不修复 Visual Studio。
  • 为什么你认为read模板应该被发现?它出现在调用者之后,并且不在调用的任何关联类型的命名空间中。
  • 如果我们删除read(const FileNode&amp; node, bool&amp; value, bool default_value),它就能找到它,所以我希望它在该功能存在时也能工作
  • 以下划线和大写字母开头的标识符,如_Tp,为标准库保留,禁止用户代码使用。

标签: c++ templates namespaces argument-dependent-lookup


【解决方案1】:

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&lt;T&gt; 的声明移到cv::operator &gt;&gt; 上面解决问题也将考虑使用ADL。

Demo

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-26
      • 2011-05-26
      • 2021-08-29
      • 2023-01-13
      • 2017-05-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多