将模板模板参数限制为两种类型之一

Constrain template template parameter to be one of two types

我有以下 classes:

template <typename T, int N0, int N1, int N2>
struct A{};

template <typename T, int N0, int N1, int N2>
struct B{};

我希望模板函数只能采用以下两种类型之一:

template <typename AorB>
void foo(AorB& arg)
{
}

其中所有 A 和 B 都被接受。 解决这个问题的最佳方法是什么?

编辑:这适用于使用继承的 base-classes。 A<...> 会有一些派生-class A_derived,而 B<...> 会有一些派生-class B_derived。如何将 AorB 限制为类型 A<...> 或 B<...>?

您可以像这样定义辅助变量:

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;

然后函数 foo 用于 is_ab 或不:

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";
}

如果您需要其他类型使用第一个重载,您可以为它们定义 is_ab,如图所示。

完整示例 here.

或者,正如@NathanOliver 评论的那样,使用一个概念。

一个简单的方法来实现你想要的是在你的基础中定义一个具有指定名称的类型 类 在这种情况下 is_ab 然后将通过 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;};

为了测试 is_ab 成员,我们使用

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;

现在就像

一样简单
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
}

如果您的继承是 protected/private,这将不起作用,并且会触发断言。

Florestan 的回答最适合您的问题,但我认为它可以作为替代方案。

#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> {};

这种方法是因为我不知道如何将类型和非类型模板参数分开。

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) {}

测试

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
}

测试不同的长度

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
}