【问题标题】:Friend functions of a class template类模板的友元函数
【发布时间】:2011-07-15 06:44:38
【问题描述】:

我有一个课程模板Foo<T>

我想实现一个非成员函数Bar,它接受两个Foos 并返回一个Foo。我希望Bar 成为非会员,因为调用者写Bar(f1, f2)f1.Bar(f2) 更自然。我还希望Bar 成为inline,因为计算简单且频繁。

template <typename T>
inline Foo<T> Bar(const Foo<T> &lhs, const Foo<T> &rhs) {
  ...
}

诀窍是Bar 需要访问Foo 的私人数据。我不希望有私有数据的访问者——没有充分的理由向用户公开私有数据。所以我想让Bar成为Foo的朋友。

template <typename T>
class Foo {
  ...
  private:
    T w, x, y, z;
    friend Foo<T> Bar(const Foo<T> &lhs, const Foo<T> &rhs);
};

这就是我遇到麻烦的地方。编译器抱怨:

当友元声明引用函数模板的特化时,不能使用内联说明符。

此规则是由标准强加的还是特定于 MSVC++?

这是我尝试过的:

  • Bar 设为 const 公共成员函数,然后声明一个仅返回 lhs.Bar(rhs) 的非成员版本。这似乎是最简单的解决方案。

  • 删除inline 提示,知道编译器将决定内联而不管提示。那么这是否违反了单一定义规则?它仍然必须在头文件中定义,因为它是一个函数模板。

  • 使用虚拟模板类型声明成员函数:

    template <typename T>
    class Foo {
      ...
      private:
        T w, x, y, z;
    
        // Note that this declaration doesn't actually use Dummy.  It's just there to
        // satisfy the compiler.     
        template <typename Dummy>
        friend Foo<T> Bar(const Foo<T> &lhs, const Foo<T> &rhs);
    };
    

我不完全确定为什么会这样,但它确实满足了编译器。

有没有更好的解决方案?

【问题讨论】:

    标签: c++ friend function-templates non-member-functions


    【解决方案1】:

    如果计算是微不足道的,我会写:

    template <typename T>
    class Foo {
      ...
      private:
        T w, x, y, z;
      public:
        friend Foo Bar(const Foo &lhs, const Foo &rhs) {
            ...
        }
    };
    

    这不会违反 ODR - 它是具有外部链接的内联函数(3.2/5 将其从 ODR 中排除,但定义相同,7.1.2/3 表示它是内联的)。

    但是,这并没有定义函数模板Bar&lt;T&gt;,它只是为Bar 定义了一组函数重载。问题中未说明的某些原因可能意味着这对您不起作用,因为您实际上需要模板。

    【讨论】:

    • 我没想到,它很好地解决了问题,满足了所有约束。
    【解决方案2】:

    我最喜欢选项 1:

    template <typename T>
    inline Foo<T> Bar(const Foo<T> &lhs, const Foo<T> &rhs) {
        return lhs.Bar(rhs);
    }
    
    template <typename T>
    class Foo {
      ...
        Foo<T> Bar(const Foo<T> &other) const;
      private:
        T w, x, y, z;
    };
    

    然后该功能被安全地包含在类中,但为了方便起见,您提供了一个包装函数。

    【讨论】:

    • +1。对于我的具体情况,我越来越喜欢这个。我仍然对一般情况感到好奇。
    【解决方案3】:

    Bar 是一个模板,所以它也必须是朋友声明中的一个模板。

    您不一定必须使用虚拟参数,而是可以使用

     template <typename U>
     friend Foo<U> Bar(const Foo<U> &lhs, const Foo<U> &rhs);
    

    这里不能使用 T 作为模板参数,因为范围内已经有一个外部 T。

    【讨论】:

    • 我试过了,但 Foo 为 Bar 声明一个友元函数似乎不正确,其中 U 与 T 的类型不同。可能没有真正的危害,但是感觉不对。这就是我最终得到虚拟参数的方式。
    • 这可能看起来不对,但目前 (C++03) 你不能为特定的 T 选择你的朋友。你的 Dummy 参数也可以是任何不同于 T 的类型。
    猜你喜欢
    • 2010-12-19
    • 1970-01-01
    • 2016-10-19
    • 2013-09-18
    • 1970-01-01
    • 1970-01-01
    • 2012-01-23
    • 1970-01-01
    相关资源
    最近更新 更多