同样的代码可以被clang编译,但在gcc中会失败

The same code gets compiled by clang but fails in gcc

本文关键字:但在 gcc 失败 编译 clang 代码      更新时间:2023-10-16

是我还是编译器错了?我试图在gcc下编译这个修复,但找不到方法。错误消息很简单,FactorialTree<T, 0>::print是私有的,但是为什么clang没有问题地接受它?另外,我如何为gcc修复这个问题?

#include <ostream>
#include <cstring>
template<typename T, std::size_t nChildren>
class FactorialTreeBase
{
protected:
  FactorialTreeBase<T, nChildren + 1> *parent;
public:
  T data;
  FactorialTreeBase(FactorialTreeBase<T, nChildren + 1> *p, const T &t)
  : parent(p)
  , data(t)
  {
  }
  const FactorialTreeBase<T, nChildren + 1> *getParent() const
  {
    return parent;
  }
  FactorialTreeBase<T, nChildren + 1> *getParent()
  {
    return parent;
  }
protected:
  static void printIndents(std::ostream &os, std::size_t nIndents)
  {
    for (std::size_t i = 0; i < nIndents; ++i)
    {
      os << "  ";
    }
    os << "+-";
  }
};
template<typename T, std::size_t nChildren>
class FactorialTree
: public FactorialTreeBase<T, nChildren>
{
  friend class FactorialTree<T, nChildren + 1>;
public:
  FactorialTree<T, nChildren - 1> *children[nChildren];
  FactorialTree(FactorialTree<T, nChildren + 1> *p = nullptr, const T &t = T())
  : FactorialTreeBase<T, nChildren>(p, t)
  {
    std::memset(children, 0, nChildren * sizeof *children);
  }
  FactorialTree(const FactorialTree<T, nChildren> &) = delete;
  FactorialTree<T, nChildren> &operator=(const FactorialTree<T, nChildren> &) = delete;
  FactorialTree(FactorialTree<T, nChildren> &&) = delete;
  FactorialTree<T, nChildren> &operator=(FactorialTree<T, nChildren> &&) = delete;
  ~FactorialTree()
  {
    for (std::size_t i = 0; i < nChildren; ++i)
    {
      if (children[i])
      {
        delete children[i];
      }
    }
  }
  friend std::ostream &operator<<(std::ostream &os, const FactorialTree<T, nChildren> &ft)
  {
    for (std::size_t i = 0; i < nChildren; ++i)
    {
      if (ft.children[i])
      {
        ft.children[i]->print(os, 0);
      }
    }
    return os;
  }
private:
  void print(std::ostream &os, std::size_t nIndents) const
  {
    this->printIndents(os, nIndents);
    os << this->data << 'n';
    for (std::size_t i = 0; i < nChildren; ++i)
    {
      if (children[i])
      {
        children[i]->print(os, nIndents + 1);
      }
    }
  }
};
template<typename T>
class FactorialTree<T, 0>
: public FactorialTreeBase<T, 0>
{
  friend class FactorialTree<T, 1>;
public:
  FactorialTree(FactorialTree<T, 1> *p = nullptr, const T &t = T())
  : FactorialTreeBase<T, 0>(p, t)
  {
  }
private:
  void print(std::ostream &os, std::size_t nIndents) const
  {
    this->printIndents(os, nIndents);
    os << this->data << 'n';
  }
};
#include <iostream>
enum
{
  N = 3
};
template<std::size_t n>
void fillTree(FactorialTree<int, n> *ft)
{
  for (std::size_t i = 0; i < n; ++i)
  {
    ft->children[i] = new FactorialTree<int, n - 1>;
    ft->children[i]->data = i;
    fillTree(ft->children[i]);
  }
}
template<>
void fillTree(FactorialTree<int, 0> *)
{
}
template<std::size_t n>
void printAndCutTree(FactorialTree<int, n> *ft)
{
  std::cout << *ft << 'n';
  for (std::size_t i = 1; i < n; ++i)
  {
    delete ft->children[i];
    ft->children[i] = nullptr;
  }
  printAndCutTree(ft->children[0]);
}
template<>
void printAndCutTree(FactorialTree<int, 0> *)
{
}
int main()
{
  FactorialTree<int, N> t;
  fillTree(&t);
  printAndCutTree(&t);
}

Clang不应该编译这个。FactorialTree<T, 0>是一个专业,在其中你不确认operator<<的友谊。专门化不与泛型情况共享代码,因此您的operator<<无法看到专门化的私有字段。对此的一个解决方案是使operator<<成为模板,然后将其特化并使其成为FactorialTree<T, nChildren>FactorialTree<T, 0>的朋友:

// forward declaration of the FactorialTree
template<typename T, std::size_t nChildren>
class FactorialTree;
// operator<< is now a template, rather than an overload
template<typename T, std::size_t nChildren>
std::ostream &operator<<(std::ostream &os, const FactorialTree<T, nChildren> &ft)
{
  for (std::size_t i = 0; i < nChildren; ++i)
  {
    if (ft.children[i])
    {
      ft.children[i]->print(os, 0);
    }
  }
  return os;
}
// the generic case
template<typename T, std::size_t nChildren>
class FactorialTree
: public FactorialTreeBase<T, nChildren>
{
  friend class FactorialTree<T, nChildren + 1>;
  // specialising operator<< and making it a friend:
  friend std::ostream &operator<< <>(std::ostream &os, const FactorialTree<T, nChildren+1> &ft);
  // ...
}
// the specialisation
template<typename T>
class FactorialTree<T, 0>
: public FactorialTreeBase<T, 0>
{
  friend class FactorialTree<T, 1>;
  // again, specialising operator<< and making it a friend
  friend std::ostream &operator<< <>(std::ostream &os, const FactorialTree<T, 1> &ft);
  // ...
}
相关文章: