This is NOT a duplicate of link
Consider the following code:
#include <type_traits>
template <typename... Bases>
struct Overloads : public Bases... {};
template <typename T>
struct A {
using AType = T;
};
template <typename T>
struct B {
using BType = T;
};
template <typename T>
struct C {
using CType = T;
};
template <typename OverloadsType>
struct Derived : public OverloadsType {
};
int main() {
// OK
static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::AType, int>);
// OK
static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::BType, float>);
// OK
static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::CType, char>);
// ???
static_assert(std::is_same_v<typename Derived<Overloads<B<float>, C<char>>>::AType, void>);
}
Demo: Link
For the last line, I need to detect that Derived<Overloads<B<float>, C<char>>>
is NOT derived from A
, so I want typename Derived<Overloads<B<float>, C<char>>>::AType
to be void
or something (it fails to compile)
How can I do this?
CodePudding user response:
My precise use case is: 1. Determine if Derived is derived from
A<T>
for someT
. 2. If so, figure out thatT
.
With C 20 concepts, this isn't too difficult. You need a function that takes A<T>
as a parameter. There need not be a function definition; we're just using template argument deduction. It will never be called:
template<typename T>
T getDerivedFromAType(A<T> const&); //Not implemented, since we will never call it.
Any type U
for which getDerivedFromAType(u)
works will either be one that is derived from A<T>
or is convertible to an A<T>
. That last one doesn't quite fit your needs, but it's more or less unavoidable. Regardless, we can build a concept out of it:
template<typename U>
concept IsDerivedFromA = requires(U u)
{
getDerivedFromAType(u);
};
So long as nobody writes an overload of getDerivedFromAType
, you're fine. This function ought to be in a detail
namespace or have something else that lets people know that it is off-limits.
To get the actual T
used by A
... well, there's a reason why the function returned T
. We simply need a using
statement that calculates the return type of the function call:
template<IsDerivedFromA T>
using BaseAType = decltype(getDerivedFromAType(std::declval<T>()));
Notice that the template is guarded by our concept.
You could even make a bigger concept that weeds out the "convertible from" loophole:
template<typename U>
concept IsDerivedFromAAndNotConvertible =
IsDerivedFromA<U> && std::derived_from<U, A<BaseAType<U>>>;