【问题标题】:Switch passed type from template从模板切换传递的类型
【发布时间】:2011-05-30 04:08:40
【问题描述】:

是否可以在 C++ 中检查传递给模板函数的类型?例如:

template <typename T>
void Foo()
{
   if (typeof(SomeClass) == T)
      ...;
   else if (typeof(SomeClass2) == T)
      ...;
}

【问题讨论】:

    标签: c++ templates types


    【解决方案1】:

    是的。您必须使用type traits。例如:

    #include <boost/type_traits/is_same.hpp>
    
    template <typename T>
    void Foo ()
    {
       if ((boost::is_same<T, SomeClass>::value))
          ...;
       else if ((boost::is_same<T, SomeClass2>::value))
          ...;
    }
    

    根据您要达到的目标,使用template specialization 可能是更好的选择。

    此外,您可以使用enable_if/disable_if 有条件地启用/禁用某些功能/方法。例如,将其与类型特征相结合将允许对一组类型使用一个函数,而对另一组类型使用另一个函数。

    【讨论】:

    • 您的代码无法编译。您要么需要将 is_same 实例化为变量,要么需要访问其内部 value 定义。
    • @Noah:对。这是一个快速而肮脏的提示 :-) 我已经修改了它。
    • 是否可以对一组值执行此操作?例如,为每个枚举值做其他事情?
    • @paulm:我不明白为什么不
    【解决方案2】:

    不,但是您可以使用部分专业化:

    template<typename T>
    struct Bar { static void foo(); };
    template<typename T>
    template<> inline void Bar<T>::foo() {
    //generic
    }
    template<> inline void Bar<int>::foo() {
    //stuff for int
    }
    template<> inline void Bar<QString>::foo() {
    //QString
    }
    

    编辑 是的,带有类型特征,但它并不是真正需要的。 编辑 2 type_traits 示例。

    #include <type_traits>
    template<typename T> void foo() {
        using std::is_same;
        if<is_same<T, T2>::value || is_same<T, T1>::value) { 
            /* stuff */
        }
    }
    

    【讨论】:

    • 如果我想做if (is_same&lt;T, T1&gt; || is_same&lt;T, T2&gt;)之类的事情怎么办?
    • 如果您的编译器支持 C++0x(VS2010/任何最近的 gcc),那么您必须使用类型特征 #include &lt;type_traits&gt; and std::is_same&lt;T, T2&gt;::value,或者只使用 boost 来实现旧编译器的兼容性。
    • 这不是部分专业化,而是完全专业化。有相当显着的差异。可能最重要的是,不可能以这种方式进行部分特化,因为它不允许用于任何类型的函数。
    【解决方案3】:

    是的,它是......但它可能不会像你期望的那样工作。

    template < typename T >
    void foo()
    {
      if (is_same<T,SomeClass>::value) ...;
      else if (is_same<T,SomeClass2>::value) ...;
    }
    

    您可以从std::boost:: 获得is_same,具体取决于您的愿望/编译器。前者仅在 C++0x 中。

    问题在于... 中的内容。如果您希望能够在 foo 中对这些类型进行特定的函数调用,那么您就大错特错了。当您传入不符合预期接口的内容时,即使该部分代码从未运行,也会导致编译器错误。

    要解决这个问题,您需要做一些不同的事情。我推荐标签调度:

    struct v1_tag {};
    struct v2_tag {};
    
    template < typename T > struct someclass_version_tag;
    template < > struct someclass_version_tag<SomeClass> { typedef v1_tag type; };
    template < > struct someclass_version_tag<SomeClass2> { typedef v2_tag type; };
    
    void foo(v1_tag) { ... }
    void foo(v2_tag) { ... }
    template < typename T > void foo()
    {
      typedef typename someclass_version_tag<T>::type tag;
      foo(tag());
    }
    

    请注意,您不会在此处遭受任何运行时多态性开销,并且启用优化后,它应该会产生相同甚至更小的代码大小和速度(尽管在运行之前您不应该担心这一点)分析器)。

    【讨论】:

    • 是否可以根据version_tag返回不同的类型?
    • 当然。如果您卡在 C++03 中,或者在 C++11 及更高版本中使用 auto,您需要想出一些方法来查询信息。只要您不尝试在同一个实例化中返回不同的类型,您就没有问题。
    • 现在使用 C++17,您可以使用 constexpr if 语句轻松完成此操作
    【解决方案4】:

    如果您想根据类型做一些特定的事情,请专门化模板:

    template <typename T>
    void Foo() { }
    
    template <>
    void Foo<SomeClass>() { }
    
    template <> 
    void Foo<SomeClass2>() { }
    
    // etc.
    

    (不过,您实际上并不想专门化函数模板;这只是为了说明。如果可以的话,您要么想要重载模板,要么委托给专门的类模板。有关原因的更多信息和如何避免专门化函数模板,阅读 Herb Sutter 的 Why Not Specialize Function Templates?)

    【讨论】:

      猜你喜欢
      • 2012-01-03
      • 1970-01-01
      • 1970-01-01
      • 2019-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多