Omer Rosler
Omer Rosler

Reputation: 247

Dependency in template friend of template class

After hours of trying to find out this syntax by reading the standart I gave up on trying to understand this. I have a template class with a template constructor I need to partially specialize, since this is impossible I wrapped the constructor implementation in a diffrent template:

template<class T> 
struct A; //forward declaration
namespace impl {
template<class T>
struct wrap {
    template<class U>
    struct AConstructor {
        static void construct(A<T>*thisPtr) {...}
    };
    //some partial specialization...
};
template<class T>
struct dummy{};
} //end of namespace
template<class T>
struct A {
   template<class U>
    A(dummy<U>) {
        impl::template wrap<T>::template AConstructor<U>::construct(this);
    }
};

This would have worked fine, but I need to access the private members of A<T> in the constructor implementation so I need a friend declaration, and this is wehere it gets tricky. I tried with or without the template keyword in possible locations and got compilation errors is visual studio: error C2992 (invalid or missing template parameter list) in the first and error C2952 (template declaration missing template parameter list) in the others:

template<class T>
class A {
    //1
    template<class U>
    friend struct impl::template wrap<T>::template AConstructor;
    //2,3 I also tried removing the static identifier with this declaration
    template<class U>
    friend void impl::template wrap<T>::template AConstructor<U>::construct(A<T>*);

How can I friend this function/struct?

Upvotes: 0

Views: 211

Answers (1)

Caninonos
Caninonos

Reputation: 1234

[Edit: The following works with both g++ 4.9.0 and clang 3.5 on my machine:

template<class T>
struct dummy {};

template<class T>
struct A {
private:
  template<class U>
  struct AConstructor {
    static void construct(A<T>* thisPtr) {
      thisPtr->foo = 42;
    }
  };

  template<class>
  friend class AConstructor;

public:
  template<class U>
  A(dummy<U>) {
    AConstructor<U>::construct(this);
  }

  void print() const {
    std::cout << foo << std::endl;
  }

private:
  int foo;
};

int main() {
  A<void> foobar( (impl::dummy<void>()) );
  foobar.print();
  return 0;
}

]

For me, this:

template<class T> 
struct A; //forward declaration

namespace impl {

  template<class T>
  struct wrap {
    template<class U>
    struct AConstructor {
      static void construct(A<T>* thisPtr) {
        thisPtr->foo = 42;
      }
    };
    //some partial specialization...
  };

  template<class T>
  struct dummy{};

} //end of namespace

template<class T>
struct A {
  template<class>
  friend class impl::wrap<T>::AConstructor;

  template<class U>
  A(impl::dummy<U>) {
    impl::template wrap<T>::template AConstructor<U>::construct(this);
  }

  void print() const {
    std::cout << foo << std::endl;
  }

private:
  int foo;
};

int main() {
  A<void> foobar( (impl::dummy<void>()) );
  foobar.print();
  return 0;
}

compiles with g++ 4.9.0 and work as expected (it displays 42 on stdout) although the compilation fails with clang 3.5, i'd like to say that it is a clang bug, but i must admit i am not really sure of that. Still, you could find that useful if that was the case:
Class template with template class friend, what's really going on here?
Template friend

However, i don't really like that design, are you sure you really need partial specialization of the constructors? You can't discard them with SFINAE, or access some subtypes with a trait class (or any other thing you want partial specialization for which can be replaced by something else)?
For instance one of these constructions:

#include <list>
#include <vector>

template<class T> 
struct is_vector : std::false_type {};

template<class T>
struct is_vector<std::vector<T>> : std::true_type {};

struct A {
  template<
    class T, typename std::enable_if<is_vector<T>::value, void*>::type = nullptr
  >
  A(T) {
    std::cout << "construction with a vector" << std::endl;
  }

  template<class T>
  A(std::list<T>) {
    std::cout << "construction with a list of " << typeid(T).name();
    std::cout << std::endl;
  }

  template<
    class T,
    typename std::enable_if<!is_vector<T>::value, void**>::type = nullptr
  >
  A(T) {
    std::cout << "construction with an iterator (supposedly) whose value_type "
                 "is";
    std::cout << typeid(typename std::iterator_traits<T>::value_type).name();
    std::cout << std::endl;
  }
};

int main() {
  A(std::vector<int>());
  A(std::list<std::vector<int>>());
  A((char*) nullptr);
  return 0;
}

which displays this on my machine:

construction with a vector
construction with a list of NSt3__16vectorIiNS_9allocatorIiEEEE
construction with an iterator (supposedly) whose value_type isc

(of course i don't know exactly what you want to do, so i'm not sure, i'd have commented about this rather than put it in an answer, but i don't have enough reputation to do so, account created today)

Upvotes: 2

Related Questions