当前位置: 首页 > 知识库问答 >
问题:

匿名命名空间内运算符的模板重载解析

曾嘉言
2023-03-14

简短的问题:运营商是否有特殊的模板查找规则,用于通过内部链接进行重载解决,或者底部的代码是否是GCC中操作员的模板重载解决错误?

细节:我不会粘贴一大块代码,而是带你完成我的推理。让我们从一些简单的代码开始:

#include <iostream>

template<typename T> struct A{ T b; };
struct B{};

template<typename T> 
void foo (const A<T>&a) { foo(a.b); } 
void foo (const B&) { std::cout << "hello"; }

int main(){
  A<B> b;
  foo(b);
}

上面印着“你好”,一切都很好。

现在,让我们将两个 foo 放在一个匿名命名空间中:

namespace {
template<typename T> 
void foo (const A<T>&a) { foo(a.b); } 
void foo (const B&) { std::cout << "hello"; }
}

代码现在无法编译。Clang表示错误:调用函数“foo”,该函数在模板定义中既不可见,也未通过参数相关查找找到。和GCC模板参数推导/替换失败。

这是坏的,因为foo(const B)

[basic.link]未命名的命名空间或直接或间接在未命名的命名空间中声明的命名空间具有内部链接。所有其他命名空间都有外部链接。

[temp.point]依赖于模板参数的表达式的实例化上下文是在同一翻译单元中的模板专用化实例化点之前声明的具有外部链接的声明集。

[temp.dep.candidate] 对于后缀表达式是依赖名称的函数调用,使用通常的查找规则 (3.4.1, 3.4.2) 找到候选函数,但以下情况除外:

>

  • 对于使用非限定名称查找(3.4.1)的查找部分,只能找到模板定义上下文中的函数声明。

    对于使用关联命名空间(3.4.2)的查找部分,只能在模板定义上下文或模板实例化上下文中找到函数声明。

    现在,使用运算符也是一样的:

    struct ostream {} cout; 
    template<typename T> struct A{ T t; };
    struct B{};
    
    namespace {
    template<typename T>
    ostream& operator<< (ostream& out, const A<T>&v) 
      { return out << v.t; }
    ostream& operator<< (ostream& out, const B&) 
      { return out; }
    }
    
    int main(){
      A<B> a;
      cout << a; 
    }
    

    GCC(4.7/4.8/4.9)现在对该代码非常满意,并使用-Wall-Wextra-pedantic-ansi发出零警告,而clang则抱怨的操作员

    我没有在标准中找到运算符重载查找的任何例外,所以我相信这是GCC中的一个错误(功能?),但模板解析规则并不容易,所以我想我可能会在提交错误之前检查这里。

    您可以在此处实时查看此代码。


  • 共有1个答案

    夹谷野
    2023-03-14
    匿名用户

    这绝对是gcc的bug。下面的代码正确地用clang打印了< code>right,但用GCC打印了< code > error 。

    #include <iostream>
    
    template<typename T> struct A{ T t; };
    struct B{};
    struct C : public B{};
    
    std::ostream& operator<< (std::ostream& out, const B&) 
      { return out << "right"; }
    
    namespace {
    template<typename T>
    std::ostream& operator<< (std::ostream& out, const A<T>&v) 
      { return out << v.t; }
    
    std::ostream& operator<< (std::ostream& out, const C&) 
      { return out << "wrong"; }
    }
    
    int main(){
      A<C> a;
      std::cout << a;
    }
    

    在此处报告。

     类似资料:
    • VS 我习惯了第二种方法,但第一种方法是等价的吗?

    • 我在运算符过载时遇到问题 主要是我有 其中<code>Integer</code>只是<code>int</code>的包装,带有我需要的一些特殊功能。 然而,当我编译上面的代码时,我得到了错误C2679,它表示<code>binary' 我还试图删除友元声明中的参数,因此代码变成了: 但这会产生另一个错误:C2785:

    • 我尝试像这样解析xml: 我尝试解析这个xml: 但是我的jobNode总是空的。如果我把我的xml改成这样,它就可以工作了:

    • 内联命名空间旨在通过”版本”的概念,来实现库的演化。考虑如下代码: // 文件:V99.h inline namespace V99 { void f(int); // 对V98版本进行改进 void f(double); // 新特性 // … } // 文件:V98.h namespace V98 { void

    • 内核命名空间 Docker 容器和 LXC 容器很相似,所提供的安全特性也差不多。当用 docker run 启动一个容器时,在后台 Docker 为容器创建了一个独立的命名空间和控制组集合。 命名空间提供了最基础也是最直接的隔离,在容器中运行的进程不会被运行在主机上的进程和其它容器发现和作用。 每个容器都有自己独有的网络栈,意味着它们不能访问其他容器的 sockets 或接口。不过,如果主机系统

    • 在这一节中,我们将探索JavaScript中关于命名空间的模式。命名空间可被看作位于一个唯一标识符下的代码单元的逻辑组合。标识符可以被很多命名空间引用,每一个命名空间本身可以包含一个分支的嵌套命名空间(或子命名空间)。 在应用开发过程中,出于很多原因,我们都要使用命名空间。在JavaScript中,它们帮助我们避免在全局空间中于其他对象或者变量出现冲突。它们对于在代码库中组织功能块也非常有用,这样