I have the following classes:
template <typename T, int N0, int N1, int N2>
struct A{};
template <typename T, int N0, int N1, int N2>
struct B{};
I want templated functions to only be able to take one of these two types:
template <typename AorB>
void foo(AorB& arg)
{
}
Where all A<T,N0,N1,N2> and B<T,N0,N1,N2> are accepted. What is the best way to approach this?
Edit: This is intended to work for base-classes where inheritance is used. A<...> would have some derived-class A_derived<N0, N1, N2>, and B<...> would have some derived-class B_derived<N0, N1, N2>. How would AorB be constrained to only be of type A<...> or B<...>?
CodePudding user response:
You could define a helper variable like this:
template<typename T>
constexpr static bool is_ab = false;
template <typename T, int N0, int N1, int N2>
constexpr static bool is_ab<A<T, N0, N1, N2>> = true;
template <typename T, int N0, int N1, int N2>
constexpr static bool is_ab<B<T, N0, N1, N2>> = true;
And then the function foo
for either is_ab
or not:
template <typename T, std::enable_if_t<is_ab<T>, int> = 0>
void foo(T arg)
{
std::cout << "AB\n";
}
template <typename T, std::enable_if_t<(!is_ab<T>), int> = 0>
void foo(T arg)
{
std::cout << "not AB\n";
}
If you need other types use the first overload, you can just define the is_ab
for them as shown.
Full example here.
Or, as @NathanOliver commented, use a concept.
CodePudding user response:
A simple way to achieve what you want would be to define a type with a specified name inside your base classes in this case is_ab
which then will be tested via SFINAE.
template <typename T, int N0, int N1, int N2>
struct A{using is_ab = int;};//the type doesn't matter
template <typename T, int N0, int N1, int N2>
struct B{using is_ab = int;};
and to test for is_ab member we use
template<typename T, typename = void> struct is_ab : std::false_type{};
template<typename T> struct is_ab<T, std::void_t<decltype(typename T::is_ab{})>> : std::true_type{};
template<typename T> constexpr bool is_ab_v = is_ab<T>::value;
now it is as simple as
template <int N0, int N1, int N2>
struct AD: A<int, N0, N1, N2>{};
template <int N0, int N1, int N2>
struct BD: B<int, N0, N1, N2>{};
template<typename T>
auto foo(T& a)
{
static_assert(is_ab_v<T>);
//...
}
int main()
{
A<int,1,2,3> a;
B<bool,3,4,5> b;
AD<3,4,3> ad;
BD<3,2,1> bd;
int r = 4;
foo(a);
foo(b);
foo(ad);
foo(bd);
//foo(r); fail
}
This wont work if your inheritance is protected/private and will trigger the assert.
CodePudding user response:
Florestan's answer is best for your question, but I think it could be a alternative.
#include <utility>
template <auto v>
struct w {};
template <typename T, typename w1, typename w2, typename w3>
struct A {};
template <typename T, typename w1, typename w2, typename w3>
struct B {};
// and derived A, B
template <typename T, typename w1, typename w2, typename w3>
struct DerivedA : public A<T, w1, w2, w3> {};
template <typename T, typename w1, typename w2, typename w3>
struct DerivedB : public B<T, w1, w2, w3> {};
This approach is because I don't know how to separate type and non-type template parameters together.
template <
template <typename... Args> typename Declare,
typename... Arguments_pack
>
constexpr bool is_AorB_type(Declare<Arguments_pack...> type) // separate declare and argument
{
return std::is_same<Declare<Arguments_pack...>, A<Arguments_pack...>>::value
|| std::is_same<Declare<Arguments_pack...>, B<Arguments_pack...>>::value;
}
template <typename AorB>
concept concept_is_AorB_type = (is_AorB_type(*(AorB*)(0)));
template <typename AorB>
requires concept_is_AorB_type<AorB>
void foo(AorB& arg) {}
for test
void first_test()
{
constexpr A<int, w<0>, w<0>, w<0>> palk = *(A<int, w<0>, w<0>, w<0>>*)(0);
constexpr bool palk_result = is_AorB_type(palk);
using type2 = std::enable_if<is_AorB_type(palk)>::type;
A<int, w<0>, w<0>, w<0>> a;
B<int, w<0>, w<0>, w<0>> b;
DerivedA<int, w<0>, w<0>, w<0>> da;
DerivedB<int, w<0>, w<0>, w<0>> db;
constexpr bool a_is = is_AorB_type(a); // true
constexpr bool b_is = is_AorB_type(b); // true
constexpr bool da_is = is_AorB_type(da); // false
constexpr bool db_is = is_AorB_type(db); // false
foo(a);
foo(b);
foo(da); // error
foo(db); // error
}
test for different length
template <typename A, typename B>
struct structs_of_different_lengths{};
void other_test()
{
structs_of_different_lengths<w<0>, w<0>> sdl;
foo(sdl); // error
}