【问题标题】:Force type of C++ templateC++ 模板的强制类型
【发布时间】:2010-03-17 06:13:27
【问题描述】:

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

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

MyClass<std::string> a; // OK
MYCLass<short> b;       // OK
MyClass<double> c;      // not OK

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

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

谢谢。

【问题讨论】:

  • 我很好奇,你有什么用例来禁止你的模板的一些实例化?我不知道为什么我不希望我的代码被重用:-/
  • “可能有所不同”是什么意思?它们在编译时可能会有所不同,还是只是与示例中的不同?
  • @Seb:模板可能无法与某些类型正常工作。它可能无法编译(在这种情况下,可能需要比编译器生成的更清晰的错误消息),或者它可能编译但没有所需的语义(在这种情况下,防止它编译的方法很好)

标签: c++ templates


【解决方案1】:

另一个版本是为禁止类型保留未定义

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

【讨论】:

  • +1。事实上,我比第一个答案更喜欢这个解决方案。
  • 不客气。无论如何,自从我上次发表评论以来,您的答案成为了选定的答案,所以显然,我不是唯一一个看到它的值的人...... :-P
  • 我真的很喜欢这种方法,但是我们怎样才能避免污染...Allowed 现在在 .h 中定义,如果我们在两个类上使用这种方法,它们可能会发生冲突。简单地称它为Allowed_MyClass?使用一些内部类?
  • @John 是的。只需使用命名空间,然后说MyClassD::Allowed 或使用不同名称的方法。很多可能性:)
  • @DrewNoakes 可能会派生自 std::true_typestd::false_type 并应用 static_assert(is_allowed&lt;T&gt;(), "Not allowed");
【解决方案2】:

只是一个快速的想法,我相信有更好的方法:

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

不过,最好在您的代码上添加一个粗注释,以防止用户使用错误的类型进行实例化 :-)

【讨论】:

  • 你打败了我。这里使用的成语称为type traits
  • 是否有另一种方法可以做到这一点,而不会在每个实例中产生 char[1] 数组的成本?就个人而言,我会使用 BOOST_STATIC_ASSERT。
  • 我非常喜欢使用 switch 语句。 void isfine() { switch (0) { case -1: case Protector&lt;T&gt;::result }; }
  • @Alexandre:我可能会使用白名单而不是黑名单,因为很难猜测用户将创建哪些类型。
【解决方案3】:

【讨论】:

    【解决方案4】:

    通常没有必要限制模板可以实例化的类型。模板可以用给定的类型编译(并且可以正常工作),或者不能(并且在程序员不做任何努力的情况下产生编译器错误)。


    如果您需要设置限制,通常这些类型有一些共同点,可以通过一些已经可用的类型特征(标准库,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;
    }
    

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

    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】:

      有多种技巧可以检查某些内容,具体取决于您允许或不允许实例化的标准。在实践中,您应该为 Boost 的概念检查之类的应用使用更高级别的库。

      【讨论】:

        【解决方案6】:

        对此我不确定,但您可以为 double 添加另一个模板特化 模板

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

        这适用于您的示例,但不适用于一般情况。

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

        希望对你有帮助。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-14
          • 1970-01-01
          • 2021-10-30
          • 2021-10-01
          相关资源
          最近更新 更多