【问题标题】:How do I declare a template friend function that is type-specific in my template class?如何在我的模板类中声明一个特定于类型的模板友元函数?
【发布时间】:2015-04-07 22:34:54
【问题描述】:

我最近了解到有两种方法可以声明模板友元类或函数。例如,要声明一个模板友元类,你可以这样做

template <typename T>
class goo
{
    template <typename T>
    friend class foo;
};

或者这个

template <typename T>
class goo
{
    friend class foo <T>;
};

这两个声明实际上是不同的。前者允许您将任何类型的模板友元类 foo 与任何类型的模板友元类 goo 一起使用。而后者只允许您使用相同的类型,这样您就可以使用 foo&lt;int&gt;goo&lt;int&gt; 而不是 foo&lt;int&gt;goo&lt;char&gt;

在下面的头文件中,我尝试使用后一种形式的声明来使我的模板友元函数friend std::ostream&amp; operator&lt;&lt;(std::ostream&amp;, const Array&lt;T&gt;&amp;); 更加特定于类型,以使我的程序更加封装。

//ARRAY_H

#include <iostream>
#include "Animal.h"

const int DefaultSize = 3;

template <typename T> // declare the template and the paramenter
class Array               // the class being parameterized
{
public:
  Array(int itsSize = DefaultSize);
  Array(const Array &rhs);
  ~Array() { delete[] pType; }

  // operators
  Array& operator=(const Array&);
  T& operator[](int offSet) { return pType[offSet]; }
  const T& operator[](int offSet) const { return pType[offSet]; }

  // accessors
  int GetSize() const { return itsSize; }

  // friend function
  friend std::ostream& operator<< <T>(std::ostream&, const Array<T>&);

private:
  T *pType;
  int itsSize;
};

template <typename T>
Array<T>::Array(int size = DefaultSize) :itsSize(size)
{
  pType = new T[size];
  for (int i = 0; i < size; i++)
      pType[i] = static_cast<T>(0);
}

Array<Animal>::Array(int AnimalArraySize) :itsSize(AnimalArraySize)
{
  pType = new Animal[AnimalArraySize];
}

template <typename T>
Array<T>::Array(const Array &rhs)
{
  itsSize = rhs.GetSzie();
  pType = new T[itsSize];
  for (int i = 0; i < itsSize; i++)
      pType[i] = rhs[i];
}

template <typename T>
Array<T>& Array<T>::operator=(const Array &rhs)
{
  if (this == &rhs)
      return *this;
  delete[] pType;
  itsSize = rhs.GetSize();
  pType = new T[itsSize];
  for (int i = 0; i < itsSize; i++)
      pType[i] = rhs[i];
  return *this;
}
template <typename T>
std::ostream& operator<<(std::ostream& output, const Array<T> &theArray)
{
  for (int i = 0; i < theArray.GetSize(); i++)
      output << "[" << i << "]" << theArray[i] << std::endl;
  return output;
}

#endif

但是,我收到编译器错误 "error C2143: syntax error : missing ';'在第 23 行的 ' 之前,即 friend std::ostream&amp; operator&lt;&lt; &lt;T&gt;(std::ostream&amp;, const Array&lt;T&gt;&amp;);

当使用前一种形式的声明时,将第 23 行更改为此

template <typename T>
friend std::ostream& operator<<(std::ostream&, const Array<T>&);

我的程序执行没有任何错误。

我假设我不能将特定类型模板友元类中的相同语法用于特定类型模板友元函数,或者我可能缺少某种前向声明。我已经搜索了堆栈溢出,我能找到的最接近这个问题的主题是here,但他们只讨论特定类型的模板友元类。我找不到讨论以这种方式使用模板友元函数的正确语法的主题。

如果这是一个语法错误,那么声明我的特定类型模板友元函数的正确方法是什么?如果这不是语法错误,为什么我的程序无法编译?

这是我的其余项目文件供您参考。我的程序所需的行为是显示参数化数组如何使用模板创建不同数组类型的多个实例。

//ANIMAL_H

#ifndef ANIMAL_H
#define ANIMAL_H

#include <iostream>

class Animal
{
public:
  // constructors
  Animal();
  Animal(int);
  ~Animal();

  // accessors
  int GetWeight() const { return itsWeight; }
  void SetWeight(int theWeight) { itsWeight = theWeight; }

  // friend operators
  friend std::ostream& operator<<(std::ostream&, const Animal&);

private:
  int itsWeight;
};

#endif

//ANIMAL.CPP

#include "Animal.h"
#include <iostream>

Animal::Animal() :itsWeight(0)
{
  std::cout << "animal() ";
}

Animal::Animal(int weight) : itsWeight(weight)
{
  std::cout << "animal(int) ";
}

Animal::~Animal()
{
  std::cout << "Destroyed an animal...";
}

std::ostream& operator<<(std::ostream& theStream, const Animal& theAnimal)
{
  theStream << theAnimal.GetWeight();
  return theStream;
}

//MAIN.CPP

#include <iostream>
#include "Animal.h"
#include "Array.h"

void IntFillFunction(Array<int>& theArray);
void AnimalFillFunction(Array<Animal>& theArray);

int main()
{
  Array<int> intArray;
  Array<Animal> animalArray;
  IntFillFunction(intArray);
  AnimalFillFunction(animalArray);
  std::cout << "intArray...\n" << intArray;
  std::cout << "\nanimalArray...\n" << animalArray << std::endl;

  std::cin.get();

  return 0;
}

void IntFillFunction(Array<int>& theArray)
{
  bool Stop = false;
  int offset, value;
  while (!Stop)
  {
      std::cout << "Enter an offset (0-9) and a value. ";
      std::cout << "(-1 to stop): ";
      std::cin >> offset >> value;
      if (offset < 0)
          break;
      if (offset > 9)
      {
          std::cout << "***Please use values between 0 and 9.***\n";
          continue;
      }
      theArray[offset] = value;
  }
}

void AnimalFillFunction(Array<Animal>& theArray)
{
  Animal *pAnimal;
  for (int i = 0; i < theArray.GetSize(); i++)
  {
      pAnimal = new Animal(i * 10);
      theArray[i] = *pAnimal;
      delete pAnimal;
  }
}

【问题讨论】:

  • 您错过了MCVE 中的M :) 重现您的问题只需要第一个 sn-p 的前半部分。
  • 我很抱歉,它不会再发生了。谢谢你的链接。

标签: c++ templates syntax compiler-errors friend-function


【解决方案1】:

在将特化称为友元之前,您需要声明函数模板。

// Forward declare class template.
template <typename T> class Array;

// Declare function template.
template <typename T>
std::ostream& operator<<(std::ostream& os, const Array<T>& arr);

template <typename T>
class Array
{
    //...
    friend std::ostream& operator<< <>(
        std::ostream& os, const Array<T>& arr);
    //...
};

【讨论】:

  • 我注意到你使用 而下面的人使用 。两者都有效,但我想知道两者之间的具体区别是什么,无论多么微不足道。帮助我理解。
【解决方案2】:

你需要一个函数模板的前向声明(就像你需要一个类模板一样)以便与一个特化成为朋友。你的代码应该是:

template <typename T>
std::ostream& operator<<(std::ostream& output, const Array<T> &theArray);

template <typename T>
class Animal
{
    // ...
    friend std::ostream& operator<< <T>(std::ostream&, const Array<T>&);
};

【讨论】:

  • 我注意到你使用 而上面的人使用 。两者都有效,但我想知道两者之间的具体区别是什么,无论多么微不足道。帮助我理解。
  • @WanderingIdiot 在这种情况下,&lt;&gt;&lt;T&gt; 之间没有区别。但是假设您正在与一个有模板和非模板重载的函数成为朋友。使用&lt;&gt; 选择模板版本,不使用它选择非模板版本。
  • 我很难理解带有模板和非模板重载的函数会是什么样子。你能通过一个例子来解释一下吗?
  • @WanderingIdiot 简单。如果您有template&lt;class T&gt; void f(T);void f(int),那么friend void f&lt;&gt;(int)T = int 成为模板重载的朋友,而friend void f(int) 与非模板重载成为朋友。
  • 我以为您试图在不同的上下文中解释 &lt;&gt;&lt;T&gt; 之间的区别。但在我看来,您的示例适用于 &lt;&gt;&lt;T&gt;。我可以理解,对于诸如template &lt;typename T&gt; class foo; 之类的模板声明,T 可以像这样删除template &lt;typename&gt; class foo;,因为在声明中根本没有使用T,因此不需要。我认为在&lt;T&gt; 中不使用T 可能有更深层次的含义,例如在我的模板声明示例中。
猜你喜欢
  • 2013-09-18
  • 2016-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-22
  • 1970-01-01
相关资源
最近更新 更多