相同的代码由clang编译但在gcc中失败

时间:2015-09-07 20:25:01

标签: c++ gcc clang

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

#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);
}

1 个答案:

答案 0 :(得分:3)

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);
  // ...
}