0

Is it me or the compiler being wrong? I tried to make a fix to get this compiled under gcc but couldn't find a way. The error message is quite straightforward that FactorialTree<T, 0>::print is private, but then why is clang accepting it with no problem? Also, how can I fix this for 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);
}
asked Sep 7, 2015 at 20:25
1
  • 3
    You should trim the code down, it includes a lot of irrelevant lines. Commented Sep 7, 2015 at 20:27

1 Answer 1

2

Clang shouldn't have compiled this. FactorialTree<T, 0> is a specialisation, and in it you do not confirm friendship for the operator<<. The specialisation does not share code with the generic case, thus your operator<< cannot see the specialisation's private fields. One solution to this would be making operator<< a template and then specialising it and making it a friend in both FactorialTree<T, nChildren> and 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);
 // ...
}
answered Sep 7, 2015 at 20:36
Sign up to request clarification or add additional context in comments.

2 Comments

Your solution works for gcc, but now fails in clang.. /tmp/a-696a74.o: In function `void printAndCutTree<1u>(FactorialTree<int, 1u>*)': a.cpp:(.text._Z15printAndCutTreeILj1EEvP13FactorialTreeIiXT_EE[_Z15printAndCutTreeILj1EEvP13FactorialTreeIiXT_EE]+0x1d): undefined reference to `operator<<(std::ostream&, FactorialTree<int, 1u> const&)'
Updated the answer, compiles on clang 3.4

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.