8

我有一个类模板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);
};

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

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

这个规则是由标准强加的还是特定于 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);
    };
    

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

有更好的解决方案吗?

4

3 回答 3

5

如果计算很简单,我会写:

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。问题中未说明可能有某种原因,这意味着这对您不起作用,因为您实际上需要模板。

于 2011-03-14T19:46:15.753 回答
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;
};

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

于 2011-03-14T19:46:43.913 回答
1

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

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

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

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

于 2011-03-14T19:48:27.180 回答