Reputation: 6616
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
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 {
. . .
};
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
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