7

Here is the (simplified) base class:

template <class T>
class SharedObject
{
protected:
    QExplicitlySharedDataPointer <typename T::Data> d;
};

And here is the derived:

class ThisWontCompile : public SharedObject <ThisWontCompile>
{
private:
    friend class SharedObject;
    struct Data : public QSharedData
    {
        int id;
    };
};

Is there any workaround to access ThisWontCompile::Data from SharedObject? What exactly can and what exactly cannot be done with the derived object from the base object?

Septagram
  • 9,425
  • 13
  • 50
  • 81

1 Answers1

13

This actually isn't related to the accessibility and friendship, it's related to the use of CRTP. Consider the following example that also exhibits the problem:

template <class T>
struct Base
{
    typedef typename T::Data Data;
};

struct ThisWontCompile : public Base<ThisWontCompile>
{
    struct Data { };
};

The problem is that ThisWontCompile is incomplete at the time it is used as a template argument to Base, so it can only be used as an incomplete type in Base.

For a handful of solutions to your specific problem, consult the answers to this other question, especially Martin's recommendation to use a traits class, which would basically look like this:

// Base
template <typename T>
struct BaseTraits;

template <typename T>
struct Base
{
    typedef typename BaseTraits<T>::Data Data;
};

// Derived
struct Derived;

template <>
struct BaseTraits<Derived>
{
    struct Data { };
};

struct Derived : public Base<Derived>
{
};

typename BaseTraits<Derived>::Data can be used in both Derived and in Base. If Derived is itself a template, you can use a partial specialization for the traits class.

Community
  • 1
  • 1
James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • So, I cannot access any derived class members at all? – Septagram Apr 04 '11 at 05:17
  • @Septagram: Not directly, no. I'd consider the traits class solution in the linked answer, if that solution works for your specific scenario. – James McNellis Apr 04 '11 at 05:21
  • 2
    @Septagram: If you state what you actually need to solve, we might be able to help there. As of your particular question, you can access members of the instantiating class in the CRTP template provided that you force a cast, and that the use of those members does not affect the memory layout of the type (i.e. cannot use a `static const int` in the instantiating type to set the size of an array in the CRTP class). I think that feature is used by Alexandrescu in Modern C++ Design in policy based design of C++ class hierarchies. – David Rodríguez - dribeas Apr 04 '11 at 08:03
  • Example of valid use: `template struct CRTP { void foo() { std::cout << static_cast(this)->x << std::endl; } }; struct test : CRTP { int x; }; int main() { test t; t.foo(); }` The trick there is that the type declaration of `CRTP` is independent on the exact definition of the `T` argument, and `T` can be incomplete when the compiler processes the line `struct test : CRTP`. – David Rodríguez - dribeas Apr 04 '11 at 08:19
  • @David, well, I guess I cannot force a cast in class definition (as opposed to class member definition). What I'm trying to do is make a little ORM combined with Qt's Explicit Sharing pattern. My goal was to let user write all the needed code, including an actual structure to store information in memory (Data), inside their subclass. – Septagram Apr 11 '11 at 10:13
  • @James, thanks for a solution. I don't need any more Traits, so I think I'll also simplify it a bit, by just putting Data on the outside and making class user to write a template specialization like you suggested. This makes my code look sort of like this: http://pastebin.com/RztSsNTc – Septagram Apr 11 '11 at 10:22