【问题标题】:friend a template specialization without <>没有 <> 的模板专业化
【发布时间】:2013-10-27 20:45:51
【问题描述】:

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

[编辑引用。第一次尝试错过了措辞的第二个差异。]

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

  1. 如果朋友的名字是合格的或不合格的template-id,朋友声明是指一个函数模板的特化,否则

  2. 如果朋友的名字是 qualified-id 并且在指定的类或命名空间中找到匹配的非模板函数,则朋友声明引用该函数,否则,

  3. [C++03:] 如果朋友的名字是一个 qualified-id 并且在指定的类或命名空间中找到了一个函数模板的匹配特化,朋友声明是指该函数模板特化,否则,

    [C++11:] 如果朋友的名字是一个 qualified-id 并且在指定的类或命名空间中找到匹配的函数模板,朋友声明引用推导的该函数模板的特化,否则,

  4. 名称应为声明(或重新声明)普通(非模板)函数的 unqualified-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);
}

当然,如果我将第 8 行更改为

friend void N::test<>(const R<T>&);

第一个项目符号适用并且程序被接受。 g++ 会打印一个有用的警告,说朋友“声明了一个非模板函数”,并建议我可能想要这样做。为了清晰和安全,代码可能也会获得更多样式点。

但是上面的代码不应该被第三个项目符号覆盖并且有效吗?朋友声明不是模板声明,它使用不是 template-idqualified-id 作为名称。并且没有与第二个项目符号匹配的非模板函数声明。

这只是两者共同的编译器错误吗?还是我误解了什么?如果有,有没有演示第三个项目符号的程序示例?

【问题讨论】:

  • +1 有趣。我怀疑&lt;&gt; 是“朋友的名字”的一部分。
  • @JohnDibling: qualified-id 包括 template-id,所以是的,f&lt;&gt;qualified-id 一共。
  • @JohnDibling 是的,标记 &lt;&gt;template-id 的一部分。 name 可以是 identifieroperator-function-idliteral-operator-id转换函数 ID模板 ID [basic/4]。
  • @aschepler:那这就是你的答案?
  • Related: Issue 674,这似乎暗示这个例子是良构的。

标签: c++ templates language-lawyer


【解决方案1】:

在 //8 行, 修改代码为: friend void N::test< R<T> >( R<T>&); 也正确。

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

我使用一些代码证明 #1 等于 #2

最后,我试着回答你的问题。我不确定这是对的。

 friend void N::test(const R<T>&);

在实例化类 R 时,R&lt;T&gt; 是已知类型。但是,功能是

声明为友元函数并且真的不实例化函数模板,那么

朋友功能是一个不存在的功能。从语法的角度来看,

编译器会提示你它是一个函数而不是一个模板

N::test (r);

在这个地方实例化了函数,但是编译器不匹配一个

朋友在R类中声明之前,因为你没有在R中声明为模板

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

【讨论】:

    【解决方案2】:

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

    在其 declarator-id 引用函数模板的特化的声明中,执行模板参数推导以识别声明所引用的特化。具体来说,这是针对显式实例化 (14.7.2)、显式特化 (14.7.3) 和某些友元声明 (14.5.4) 完成的。

    在这种情况下,declarator-id 不是特化,因此不会进行推导。

    【讨论】:

    • [temp.arg.explicit]/3 说“如果所有的模板参数都可以推导出来,那么它们都可以被省略;在这种情况下,空的模板参数列表&lt;&gt;本身也可以被省略。” declarator-id is 从来都不是特化,它是一个名称。它专业化,参见 [temp.fct.spec]/1。
    【解决方案3】:

    N::test 是一个模板函数,它接受一个名为T 的类。它不需要一个名为R&lt;T&gt; 的类。适当更改功能,它就会起作用。

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

    【讨论】:

    • 您能详细说明一下吗?为什么 [temp.friend]/3 不适用?
    • 扣除工作正常,例如对于像template void N::test(const R&lt;int&gt;&amp;); 这样的显式实例化(在全局命名空间中;保留template&lt;class T&gt; void test(const R&lt;T&gt;&amp;);)。
    • g++4.9 和 clang++3.4 reject your program(甚至是 clang++3.6)。你用的是什么编译器?也类似于what I've noticeda year ago
    • 我使用MSVC 2013。如果我们通过需求列表:1)朋友的名字没有模板化,所以我们去2。2)朋友的名字是合格的- id,但它没有匹配的非模板函数(N::test 是模板化的)所以我们得到了 3。 3) 朋友的名字是限定 ID,并且在命名空间 N 中找到匹配的函数模板,所以我们可以使用我们的函数。朋友 void N::test(const R&);来自原始代码的模板化(看到那里的 T 了吗?),所以它尝试使用第一个定义和一个不存在的函数模板的特化......
    • 除非您使用找到的修复 OP。函数模板的特化:msdn.microsoft.com/en-us/library/kwyxac18.aspx 模板类的成员函数:msdn.microsoft.com/en-us/library/80dx1bas.aspx 我不知道为什么我的代码不适合你,但看起来 OP 的修复应该。
    猜你喜欢
    • 2020-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-27
    • 1970-01-01
    • 2018-12-28
    • 2015-08-01
    相关资源
    最近更新 更多