【问题标题】:Constrain template template parameter to be one of two types将模板模板参数约束为两种类型之一
【发布时间】:2022-01-22 08:36:28
【问题描述】:

我有以下课程:

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 都被接受。 解决这个问题的最佳方法是什么?

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

【问题讨论】:

  • 我会提炼出使AB 成为函数有效类型的原因,然后创建一个类型特征或强制执行该概念的概念。这样就可以扩展代码而无需更改任何内容。新类 C 只需满足相同的约束 AB 即可。

标签: c++ templates


【解决方案1】:

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
}

【讨论】:

    【解决方案2】:

    实现您想要的一个简单方法是在您的基类中定义一个具有指定名称的类型,在本例中为 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
    }
    

    如果您的继承是受保护的/私有的并且会触发断言,这将不起作用。

    【讨论】:

      【解决方案3】:

      你可以像这样定义一个辅助变量:

      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 评论的那样,使用一个概念。

      【讨论】:

        猜你喜欢
        • 2021-03-08
        • 2021-09-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多