C++ 不使用<&燃气轮机;

C++ 不使用<&燃气轮机;,c++,templates,language-lawyer,C++,Templates,Language Lawyer,C++03和C++11在[temp.friend]的第一段中有: [已编辑的引文。第一次尝试遗漏了第二次措辞差异。] 对于不是模板声明的友元函数声明: 如果友元的名称是限定的或非限定的模板id,则友元声明引用函数模板的专门化,否则 如果友元的名称是限定id,并且在指定的类或命名空间中找到匹配的非模板函数,则友元声明引用该函数,否则 [C++03:]如果友元的名称是限定id,并且在指定的类或命名空间中找到函数模板的匹配专门化,则友元声明引用该函数模板专门化,否则 [C++11:]如果友元的名称是限

C++03和C++11在[temp.friend]的第一段中有:

[已编辑的引文。第一次尝试遗漏了第二次措辞差异。]

对于不是模板声明的友元函数声明:

  • 如果友元的名称是限定的或非限定的模板id,则友元声明引用函数模板的专门化,否则

  • 如果友元的名称是限定id,并且在指定的类或命名空间中找到匹配的非模板函数,则友元声明引用该函数,否则

  • [C++03:]如果友元的名称是限定id,并且在指定的类或命名空间中找到函数模板的匹配专门化,则友元声明引用该函数模板专门化,否则

    [C++11:]如果友元的名称是限定id,并且在指定的类或命名空间中找到了匹配的函数模板,则友元声明引用该函数模板的推断专门化,否则

  • 名称应为声明(或重新声明)普通(非模板)函数的非限定id

  • [措辞上的变化对我来说似乎是澄清。尽管我想可能有不同的方式来解释C++03中关于“在类或名称空间中查找专门化”的措辞。]

    我对第三颗子弹很好奇。我写这段代码是为了满足它的要求,但是g++4.8.1和clang++3.4都拒绝了这段代码,不管是-std=c++03还是-std=c++11:

    template <class T> class R;
    namespace N {
        template <class T> void test(const R<T>&);
    }
    
    template <class T>
    class R {
        friend void N::test(const R<T>&);  // 8
        int m;
    };
    
    template <class T>
    void N::test(const R<T>& rec) { rec.m; }
    
    int main() {
        R<int> r;
        N::test(r);
    }
    
    模板类R;
    名称空间N{
    模板无效测试(常数R&);
    }
    模板
    R类{
    friend void N::test(const R&);//8
    int m;
    };
    模板
    void N::test(constr&rec){rec.m;}
    int main(){
    R;
    N:试验(r);
    }
    
    当然,如果我把第8行改为

    friend void N::test<>(const R<T>&);
    
    friend void N::test(const R&);
    
    第一个项目符号适用,程序被接受。g++打印了一条有用的警告,说friend“声明了一个非模板函数”,并建议我可能确实要这样做。为了清晰和安全,代码可能会得到更多的样式点

    但是上面的代码不应该包含在第三个项目符号中并且有效吗?好友声明不是模板声明,并使用非模板id的限定id作为名称。并且没有与第二个项目符号匹配的非模板函数声明

    这仅仅是两者共同的编译器错误吗?或者我误解了什么,如果是的话,是否有一个程序的例子证明了第三个项目?

    我认为这是进行扣除的一个条件,因为从14.8.2.6开始

    在声明器id引用函数模板专门化的声明中,将执行模板参数推断以标识声明所引用的专门化。具体来说,这是为显式实例化(14.7.2)、显式专门化(14.7.3)和某些友元声明(14.5.4)而做的


    在这种情况下,声明器id不是专门化,因此不会进行任何推导。

    N::test是一个模板化函数,它使用一个名为
    T
    的类。它不接受名为
    R
    的类。适当地改变功能,它就会工作

    namespace N
    {
        template <class T>
        void test ( const T & );
    }
    
    template <class T>
    class R
    {
        friend void N::test ( const R<T> & );
        int m;
    };
    
    template <class T>
    void N::test ( const T & rec ) { rec.m; }
    
    int main ( )
    {
        R<int> r;
        N::test ( r );
    }
    
    名称空间N
    {
    模板
    孔隙试验(常数T&);
    }
    模板
    R类
    {
    朋友空N::测试(常数R&);
    int m;
    };
    模板
    void N::test(const T&rec){rec.m;}
    int main()
    {
    R;
    N:试验(r);
    }
    
    在第//8行, 修改代码为:
    friend void N::test(R&);
    
    也正确。

    friend void N::test<R<T>>(const R<T>&);//one type is friend with one type  #1
    friend void N::test<>(const R<T>&);// one type is friend with one type    #2
    
    在这里,函数被实例化,但编译器与

    friend在R类中声明之前,因为您没有在R类中声明为模板


    类,您只需声明一个函数。

    +1有趣。我怀疑
    是“朋友的名字”的一部分。@johndilling:qualified id包括其他备选模板id,因此是的,
    f
    是一个完全限定的id。@johndilling是的,令牌
    是模板id的一部分。名称可以是标识符、运算符和函数id,文字运算符id、转换函数id或模板id[basic/4]。@aschepler:这就是您的答案?相关:,这似乎意味着示例格式正确。[temp.arg.explicit]/3说“如果可以推导出所有模板参数,则可以省略所有模板参数;在这种情况下,空模板参数列表
    本身也可能被忽略。“声明器id从来不是专门化,它是一个名称。它指的是专门化,请参见[temp.fct.spec]/1。您能详细说明一下吗?为什么[temp.friend]/3不适用?例如,对于像
    模板void N::test(const R&);
    (在全局名称空间中;保留
    模板void test(const R&);
    )这样的显式实例化,演绎效果很好。g++4.9和clang++3.4(甚至clang++3.6)。您使用了什么编译器?它也类似于。我使用MSVC 2013。如果我们查看需求列表:1)朋友的名字没有模板化,因此我们转到2。2)朋友的名字是一个限定id,但它没有匹配的非模板函数(N::test是模板化的)因此我们得到了3.3)友元的名称是一个限定id,并且在名称空间N中找到了匹配的函数模板,因此我们可以使用我们的函数,因此它尝试使用第一个定义,并对函数模板进行专门化,这是不存在的…除非您使用找到的修复操作。函数模板的专门化:模板化类的成员函数:我不知道为什么我的代码对您不起作用,但看起来OP的修复应该如此。
     friend void N::test(const R<T>&);
    
    N::test (r);