代码之家  ›  专栏  ›  技术社区  ›  gregseth

C++模板的强制类型

  •  12
  • gregseth  · 技术社区  · 15 年前

    我有一个基本的模板类,但我想将专业化的类型限制为一组类或类型。例如。:

    template <typename T>
    class MyClass
    {
    .../...
    private:
        T* _p;
    };
    
    MyClass<std::string> a; // OK
    MYCLass<short> b;       // OK
    MyClass<double> c;      // not OK
    

    这些只是示例,允许的类型可能有所不同。

    这是可能的吗?如果是,怎么做?

    谢谢。

    6 回复  |  直到 15 年前
        1
  •  17
  •   Johannes Schaub - litb    15 年前

    另一个版本是不为禁止的类型定义它

    template<typename T>
    struct Allowed; // undefined for bad types!
    
    template<> struct Allowed<std::string> { };
    template<> struct Allowed<short> { };
    
    template<typename T>
    struct MyClass : private Allowed<T> { 
      // ...
    };
    
    MyClass<double> m; // nono
    
        2
  •  8
  •   Alexander Gessler    15 年前

    很快,我相信有更好的方法:

    template <typename T> struct protector {
    static const int result = 1;
    };
    
    template <> struct protector<double> {
    static const int result = -1;
    };
    
    template <typename T> 
    class MyClass
    {
       private:
         char isfine[protector<T>::result];
    };
    

    但是,最好在代码上加上一个胖注释,以防止用户使用错误的类型进行实例化:—)

        3
  •  6
  •   Axel Gneiting    15 年前
        4
  •  2
  •   UncleBens    15 年前

    一般来说,不需要限制可以用哪些类型模板进行实例化。模板可以使用给定的类型进行编译(并且工作正常),或者不可以(并且在程序员不做任何努力的情况下生成编译器错误)。


    如果需要设置限制,通常类型有一些共同点,可以用已经可用的类型特征来描述(标准库, boost::type_traits 或者您可以为它们创建一个新的类型特征。

    例如,这里有一个模板类,它只允许整数类型,使用 std::numeric_limits 要检查它(如果您编写自己的数字类型,您可以专门化它,以便它也可以与新的整数类型一起工作)。 static_assert 是C++0X,如果不可用的话 BOOST_STATIC_ASSERT 或者别的把戏。

    #include <limits>
    #include <string>
    
    template <class T>
    class X
    {
        static_assert(std::numeric_limits<T>::is_integer, "X can be only instantiated with integer types");
        //...
    };
    
    int main()
    {
        X<int> xi;
        X<char> xc;
        //X<double> xd;
        //X<std::string> xs;
    }
    

    如果您只打算支持少数几种没有共同点的任意类型(从您的假设示例中可以明显看出),一种方法是使用类型列表。再一次,boost可能会使任务变得更简单,但下面是您可以自己滚动的方法(这只是进行了一半,需要额外的工作才能使声明的类型列表更漂亮)。

    struct no_type {};
    
    template <class T, class U = no_type>
    struct t_list
    {
        typedef T head;
        typedef U tail;
    };
    
    //trait to check if two types are identical
    template <class T, class U>
    struct is_same
    {
        static const bool value = false;
    };
    
    template <class T>
    struct is_same<T, T>
    {
        static const bool value = true;
    };
    
    //compile-time recursion to check if T matches any type in list L
    template <class T, class L>
    struct in_type_list
    {
        static const bool value =
            is_same<T, typename L::head>::value || in_type_list<T, typename L::tail>::value;
    };
    
    //terminates recursion
    template <class T>
    struct in_type_list<T, no_type>
    {
        static const bool value = false;
    };
    
    template <class T>
    class X
    {
        typedef t_list<double, t_list<int, t_list<char> > > allowed_types; //double, int, char
    
        //poor man's static_assert
        typedef int check_type [in_type_list<T, allowed_types>::value ? 1 : -1];
        //...
    };
    
    int main()
    {
        X<char> xc;
        X<int> xi;
        X<double> xd;
        //X<float> xf;
    }
    
        5
  •  1
  •   AProgrammer    15 年前

    有各种各样的技巧可以检查一些东西,这取决于您的实例化标准是否被允许。在实践中,对于Boost的概念检查,您应该使用更高的杠杆库。

        6
  •  1
  •   Tom    15 年前

    我不确定,但您可以为double添加另一个模板专门化 模板

    class MyClass
    {
    .../...
    private:
        T* _p;
    };
    
    template <double> class MyClass
    {};
    

    这对你的例子很有用,但对一般情况不适用。

    通常,我会添加一个编译断言来检查不需要的类型。

    希望有帮助。