【问题标题】:Assert if template parameter is allowed断言是否允许模板参数
【发布时间】:2017-06-19 07:10:39
【问题描述】:

我正在尝试将模板化方法限制为给定的允许类型列表及其“重复”风格。

typedef boost::mpl::set<bool, int, double> allowedTypes;

class Foo
{
    typedef boost::mpl::set<bool, int, double> allowedTypes;
    template<class T>
    void some_templated_method()
    {
       BOOST_MPL_ASSERT((boost::mpl::has_key<allowedTypes, T>));
    }
}


// main.cpp
Foo foo;
struct restricted_type{};
foo.some_templated_method<restricted_type>(); // Compiles, why ?!

除此之外,我想知道如何自动过滤允许类型的重复版本。对于重复版本,我的意思是他们的std::vector&lt;T&gt; 表示,而没有在mpl::set 中明确表示它

例如

typedef boost::mpl::set<bool, 
                        int, 
                        double, 
                        std::vector<bool>,
                        std::vector<int>,
                        std::vector<double> > allowedTypes;

【问题讨论】:

  • 我只是想让你知道我编辑了我的答案,以便在类型转换上提供更多通用性。核心思想保持不变,但如果您需要,它将为您提供更大的灵活性:)

标签: c++ templates boost boost-mpl


【解决方案1】:

您始终可以自动创建包含原始集及其容器包装对应物的序列。

为此,我们需要一个一元 lambda:

template <template <typename...> class Container>
struct MakeContainerOfT {
    template <typename T>
    struct impl {
        using type = Container<T>;
    };
 };

这可以处理除地图(它们需要一个值类型)和数组(有一个非类型模板参数)之外的所有容器。作为奖励,这里是制造 N 大小数组的工厂:

template <std::size_t N>
struct ArrayOfT {
    template <typename T>
    struct impl {
        using type = std::array<T, N>;
    };
};

现在,我们需要一个工具,将这个 lambda(或任何其他一元 lambda)应用到我们的集合上,用于给定的任何容器。

template <typename Sequence, template <typename> class Transform>
struct TransformedSequenceBuilder {
    using type = typename boost::mpl::reverse_fold<Sequence,
                                      boost::mpl::set0<>,
                                      boost::mpl::insert<boost::mpl::_1, 
                                                         Transform<boost::mpl::_2>>>::type;
};

我们终于可以继续使用“累加器”了,它将为一个可变的转换 lambda 序列执行此操作:

template <typename Sequence, template <typename> class... Transforms>
struct MakeFullSequence;

template <typename Sequence, template <typename> class Transform, template <typename> class... Tail>
struct MakeFullSequence<Sequence, Transform, Tail...> {

    using type = typename boost::mpl::reverse_fold<typename MakeFullSequence<Sequence, Tail...>::type,
                                      typename TransformedSequenceBuilder<Sequence, Transform>::type,
                                      boost::mpl::insert<boost::mpl::_1, boost::mpl::_2>>::type;
};

template <typename Sequence>
struct MakeFullSequence<Sequence> {
    typedef Sequence type;
};

最后一步是为您感兴趣的容器定义一个别名:

template <typename Sequence>
using wrapped_set = typename MakeFullSequence<Sequence,
                                              ContainerOfT<std::vector>::template impl,
                                              ContainerOfT<std::set>::template impl//,
                                              /* any transformation you fancy here */>::type;

要对此进行测试,我们可以执行相等性测试:

using le_set = boost::mpl::set<int, double, char>;
using le_vector_set = boost::mpl::set<std::vector<int>, std::vector<double>, std::vector<char>>;
using le_set_set = boost::mpl::set<std::set<int>, std::set<double>, std::set<char>>;

using le_transformed_set = wrapped_set<le_set>;
using le_manually_transformed_set = boost::mpl::joint_view<boost::mpl::joint_view<le_set, le_set_set>::type, le_vector_set>::type;

std::cout << boost::mpl::equal<le_transformed_set,
                               le_manually_transformed_set>::value;

那么用法就很简单了:用户只提供“原始”类型Set,然后每次需要时将逻辑分支到wrapped_set&lt;Set&gt;

class Foo
{
    typedef boost::mpl::set<bool, int, double> allowedTypes;
    template<class T>
    void some_templated_method()
    {
       BOOST_MPL_ASSERT((boost::mpl::has_key<wrapped_set<allowedTypes>, T>));
    }
};

您可以在此处找到演示我们最终得到原始集、向量包装集和集包装集的并集:Live Demo

通过这种设计,您还可以添加任何其他您喜欢的“重复风味”。输入类型的引用(resp。const)对应?只需传入std::add_lvalue_reference(或std::add_const)!

【讨论】:

    【解决方案2】:

    我认为正确的方法是添加自定义类型特征,该特征应该专门用于对some_templated_method 支持的类型具有真正的价值。

    #include <string>
    #include <type_traits>
    #include <vector>
    
    template< typename T, typename TDummy = void > class
    t_GoodForSomeTemplateMethod final
    :   public ::std::integral_constant< bool, false >
    {
        //  Nothing.
    };
    
    template<> class
    t_GoodForSomeTemplateMethod< bool > final
    :   public ::std::integral_constant< bool, true >
    {
        //  Nothing.
    };
    
    template< typename TInt > class
    t_GoodForSomeTemplateMethod< TInt, ::std::enable_if_t< ::std::is_integral< TInt >::value > > final
    :   public ::std::integral_constant< bool, true >
    {
        //  Nothing.
    };
    
    template< typename TFlt > class
    t_GoodForSomeTemplateMethod< T, ::std::enable_if_t< ::std::is_floating_point< TFlt >::value > > final
    :   public ::std::integral_constant< bool, true >
    {
        //  Nothing.
    };
    
    template< typename TItem, typename TAllocator > class
    t_GoodForSomeTemplateMethod< ::std::vector< TItem, TAllocator > > final
    :   public ::std::integral_constant< bool, true >
    {
        //  Nothing.
    };
    
    template< class T > void
    SomeTemplateMethod(void)
    {
        static_assert(t_GoodForSomeTemplateMethod< T >::value, "type is not suitable for SomeTemplateMethod");
    }
    
    int
    main(void)
    {
        SomeTemplateMethod< int >(); // ok
        SomeTemplateMethod< ::std::vector< char > >(); // ok
        SomeTemplateMethod< ::std::string >(); // error
    }
    

    【讨论】:

    • 这比 mpl::set 方法复杂得多