【问题标题】:Additional template arguments for friend declaration in template class?模板类中朋友声明的附加模板参数?
【发布时间】:2020-01-28 23:14:26
【问题描述】:

为了一些全局函数

template<typename T, int count>
void func (const Obj<T>& obj) {
    for (int i = 0; i < count; i++)
        std::cout << obj.value << std::endl;
}

能够访问某些模板类的私有字段value

template<typename T>
class Obj {
    public:
        Obj (T value);
    private:
        T value;
};

template<typename T>
Obj<T>::Obj (T value) : value(value) {}

我们需要声明func&lt;T, count&gt;Obj&lt;T&gt; 的朋友。但是必须先声明func&lt;T, count&gt;,然后才能使其成为Obj&lt;T&gt; 的朋友,为此我们需要转发声明Obj&lt;T&gt;。生成的代码如下所示

// Forward declarations
template<typename T>
class Obj;

template<typename T, int count>
void func (const Obj<T>& obj);

// Obj<T>
template<typename T>
class Obj {
    public:
        Obj (T value);

        template<int count>
        friend void func<T, count> (const Obj<T>& obj);
    private:
        T value;
};

template<typename T>
Obj<T>::Obj (T value) : value(value) {} // <-- ERROR

// func<T>
template<typename T, int count>
void func (const Obj<T>& obj) {
    for (int i = 0; i < count; i++)
        std::cout << obj.value << std::endl;
}

但这让 gcc 抱怨“在主模板声明中无效使用模板 ID 'func'”,那么我如何为每个 count 声明 func&lt;T, count&gt;Obj&lt;T&gt; 的朋友?根据this answer,我只需要用这个替换朋友声明

template<typename T1, int count>
friend void func (const Obj<T1>& obj);

据我所知,无论T1T 是否匹配,这将使func&lt;T1, count&gt; 成为Obj&lt;T&gt; 的朋友,这是荒谬的。是否可以将func&lt;T, count&gt; 声明为Obj&lt;T&gt; 的朋友,而不是其他Obj&lt;T1&gt;? (最好将func&lt;T, count&gt;的定义保持在Obj&lt;T&gt;的定义之外)

(我知道我可以让count 成为一个真正的参数,但上面的示例只是对我的真实代码的简化。实际上,我试图以某种方式为某些类Obj&lt;T&gt; 重载std::basic_ostream&lt;CharT, Traits&gt;&amp; operator&lt;&lt; (std::basic_ostream&lt;CharT, Traits&gt;&amp; stream, const Obj&lt;T&gt;&amp; obj)允许operator&lt;&lt; 访问Obj&lt;T&gt; 的私有字段。)

【问题讨论】:

  • 您不需要两个前向声明中的任何一个(类Obj 或函数func)。相反,函数的 friend 声明将自己充当前向声明。
  • 不能只在类中定义函数吗?

标签: c++ templates friend


【解决方案1】:

friend 声明必须匹配任何可能的前向声明,当然还有定义,包括模板参数。

这意味着你需要例如

template<typename U, int count>
friend void func(const Obj<U>& obj);

类模板参数T 和函数模板参数U 是否不同并不重要,因为无论如何都会调用正确的函数。

例子:

Obj<int> int_obj;
Obj<float> float_obj;

func<X>(int_obj);  // Will call void func<int, X>(int_obj)
func<X>(float_obj);  // Will call void func<float, X>(float_obj)

作为替代方案,您可以在类定义中定义内联函数,然后就不需要提供TU 模板参数:

template<int count>
friend void func(const Obj<T>& obj)
{
    // Implementation...
}

在这两种情况下,您都应该拥有func 的前向声明(正如我的评论中所述)。

【讨论】:

  • 但是,如果TU 不匹配,如何确保func&lt;U, count&gt; 不是Obj&lt;T&gt; 的朋友?对我来说,这似乎表明func&lt;std::string, 5&gt;Obj&lt;int&gt; 的朋友,还是我错了?
  • 这将如何与std::basic_ostream&lt;CharT, Traits&gt;&amp; operator&lt;&lt; (std::basic_ostream&lt;CharT, Traits&gt;&amp; stream, const Obj&lt;T&gt;&amp; obj) 一起使用?
  • @Cubi73 如果Ustd::string,则声明变为friend void func(const Obj&lt;std::string&gt;&amp; obj);。是的,它将成为例如的朋友。 Obj&lt;int&gt; 但你不能将 Obj&lt;int&gt; 对象传递给函数,那么这真的很重要吗?
  • 是的,这很重要。如果我可以让func(Obj&lt;T&gt; obj) 成为仅Obj&lt;T&gt; 的朋友,而只是省略额外的count 参数,那么在包含count 参数时应该有一种方法可以做到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-09
  • 1970-01-01
  • 2014-10-03
  • 2013-09-28
  • 1970-01-01
  • 2013-09-18
相关资源
最近更新 更多