C++模板:部分模板规范和友元类

9

有没有可能将部分模板规范设置为友元类?例如,考虑您拥有以下模板类

template <class T> class X{
    T t;
};

现在您可以进行部分特化,例如指针。
template <class T> class X<T*>{
    T* t;
};

我希望实现的是,每个可能的 X<T*> 都是 X<S> 的友元类,对于任何 S。也就是说,X<A*> 应该是 X<B> 的友元。
当然,我考虑过在 X 中使用普通的模板友元声明:
template <class T> class X{
    template <class S> friend class X<S*>;
}

然而,这段代码无法编译,g++ 告诉我如下:

test4.cpp:34:15: 错误: 'template<class T> class X' 的特化必须出现在命名空间作用域

test4.cpp:34:21: 错误: 部分特化 'X<S*>' 声明为 'friend'

这个问题到底是根本不可能实现呢?还是有一些变通方法?

我之所以问,在于我需要在 X<T*> 中提供一个构造函数,该函数可以从任意的 X<S> 创建这个类(S 必须是 T 的子类型)。

代码如下:

template <class T> class X<T*>{
    T* t;

    template<class S>
    X(X<S> x) : t(&(x.t))  {} //Error, x.t is private
}

现在,编译器当然会抱怨x.t在构造函数中不可见,因为它是私有的。这就是为什么我需要一个部分特化友元类。


1
一个 get 函数真的不可能吗?这对我来说更加简洁,避免了所有模板友元的疯狂。 - pmr
它在这个例子中可能会起作用。然而,可能存在不应该向公众暴露而只能向模板特化暴露的数据。问题是是否有可能以某种方式实现这种行为。 - gexicide
2个回答

9
在C++中,您可以在四个级别上授予超出private访问权限。
完全公共访问(参见pmr的答案)
继承层次结构内的访问(protected,在此无关紧要)
对基本模板友元的访问(请参阅此答案)
对非模板或完全专门化的友元的访问(太弱以解决您的用例)
这两种友情之间没有中间道路。
来自C++标准§14.5.4:
友元声明不得声明部分特化。
以下声明将允许您实现所需内容。 它使您可以自由地从任何其他专业化中访问您的模板,并且仍然只能在X内进行访问。 它比您要求的略微宽容。
template<class T> class X
{
    template<class Any> friend class X;
    public:
        ...
};

如果其他人遇到和我一样的问题:一定要正确匹配语法。即使不是有意为之,以下代码也会报告错误尝试将部分特化设为友元:template<class Any> friend class X<Any>; 请注意与答案中正确示例相比末尾的额外括号。 - mattgately

1
我们可以定义一个由X中定义的密钥保护的getter
#include <type_traits>

template <class T> class X{
  T t;
public:
  struct Key {
    template<typename S>
    Key(const X<S>&) {
      static_assert(std::is_pointer<S>::value, "Not a pointer");
    }
  };

  const T& get(Key) const { return t; }
  T& get(Key) { return t; }
};

template <class T> class X<T*> {
  T* t;
public:
  template<class S>
  X(X<S>& x) : t(&(x.get(typename X<S>::Key(*this))))  {}
};

int main()
{
  X<int> x1;
  X<int*> x2(x1);
  return 0;
}

这仍然有一些弱点。现在,每个拥有 X<T*> 的人都可以使用 get。但是现在已经变得如此混乱,以至于没有人会意识到这一点。我会选择一个简单的公共 getter。


1
基本上,每个拥有nullptr的人都可以使用get。看起来我们在这里有一个宽容度的竞赛。 - Jirka Hanika
@JirkaHanika 你可以尝试使用 null_ptr 进行重载,但这并不能让它变得更好。 - pmr
1
是的,每个使用 0 字面量的人仍然可以使用 get - Jirka Hanika

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接