Zizheng Tai
Zizheng Tai

Reputation: 6616

Template friendship based on inheritance relationship

I'm not sure if this is achievable. Suppose I have two class templates Foo<T> and FooOwner<T>, and a FooOwner<T> has a pointer member to a Foo<U>, where U is a subclass of T.

I want to make FooOwner<T> a friend of Foo<U>, but only if U is a subclass of T. Is that possible? If not, is there any workaround for something close enough?

template<typename T>
class FooOwner {
private:
    Foo<T>* p;  // This can point to a object of type Foo<U>
                // where U is a subclass of T
};

// NOTE: Foo<Derived> is always a subclass of Foo<Base>

template<typename T>
class Foo {
private:
    template<typename U>  // Constrain U to be a superclass of T?
    friend class FooOwner;
};

Upvotes: 1

Views: 87

Answers (2)

Johan Lundberg
Johan Lundberg

Reputation: 27028

As it seems FooOwner is not part of the external interface of Foo, you can declare it in Foo:

template<typename T>
class Foo {
private:
    template<typename U>  // Constrain U to be a superclass of T?
    friend class FooOwner;

    template< typename U>
    class FooOwner {
    public:
        FooOwner(std::enable_if<std::is_base_of<T, U>::value, U*> uptr) : p(uptr){} // (rough idea) 
    private:
        Foo<U>* p;  // This can point to a object of type Foo<U>
                    // where U is a subclass of T
    };    
};

You can then use is base of testing or similar on the constructors of FooOwner or FooOwners methods (as sketched above).

More to the point, you can do this:

template<typename T>
class Foo {
private:
    template<typename U, typename = std::enable_if<std::is_base_of<T, U>::value>> 
    friend class FooOwner;

    template<typename U, typename = std::enable_if<std::is_base_of<T, U>::value>> 
    class FooOwner {
    private:
        Foo<U>* p;  // This can point to a object of type Foo<U>
                    // where U is a subclass of T
    };    
};

Both the first and second enable_if is not strictly required. You can remove the friendship completely (and the first enable if) and simply let FooOwner exist only if you the base-derived relation is the right. If you would like to allow the class to exist, and disallow only the friendship you can remove the second enable if. Keep a dummy template typename in that case:

template<typename U, typename = T> 
class FooOwner {
   . . . 
};

Live demo

If you would like to keep FooOwner external to Foo, you could make it a template of both U and T in a similar way.

Upvotes: 1

rocambille
rocambille

Reputation: 15976

I'm not sure to understand what you want to achieve, but maybe the type trait is_base_of can help you:

#include <type_traits>

class A {};

class B : A {};

class C {};

int main() 
{
    std::is_base_of<A, B>::value; // true
    std::is_base_of<C, B>::value; // false
}

You can use it with conditional:

std::conditional<std::is_base_of<A, B>::value, A, SomethingElse>::type
  // will be A if B is derived from A, or SomethingElse otherwise

Or with enable_if:

std::enable_if<std::is_base_of<A, B>::value, A>::type
  // will be defined only if B is derived from A

Upvotes: 1

Related Questions