首页 文章

类模板的朋友功能

提问于
浏览
8

我有一个类模板 Foo<T> .

我想实现一个非成员函数 Bar ,它需要两个 Foo 并返回一个 Foo . 我希望 Bar 成为非成员,因为调用者编写 Bar(f1, f2)f1.Bar(f2) 更自然 . 我也希望 Barinline ,因为计算是微不足道的 .

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

诀窍是 Bar 需要访问 Foo 's private data. I' d不想拥有私有数据的访问者 - 有's no good reason to expose the private data to users. So I' d想让 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);
};

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

有更好的解决方案吗?

3 回答

  • 5

    我最喜欢选项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;
    };
    

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

  • 2

    Bar是一个模板,因此它也必须是朋友声明中的模板 .

    您不一定要使用虚拟参数,但可以使用

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

    您不能在此处使用T作为模板参数,因为范围中已存在外部T.

  • 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<T> ,它只是为 Bar 定义了一组函数重载 . 可能有一些原因,在问题中没有说明,这意味着这对你不起作用,因为你实际上需要模板 .

相关问题