【问题标题】:C++: How to use type in template function to branch?C++:如何在模板函数中使用类型来分支?
【发布时间】:2011-09-20 21:02:18
【问题描述】:

我不太精通模板。如何编写一个名为 get 的模板函数,该函数根据模板类型选择从中获取的数组?请看下面的例子:

struct Foo
{
    int iArr[10];
    char cArr[10];

    // How to pick array here based on template type?
    template < typename T >
    T get( int idx )
    {
        // This does NOT work!
        switch ( T )
        {
        case int:
            return iArr[ idx ];
        case char:
            return cArr[ idx ];
        }
    }
};

// Expected behaviour of get()
Foo foo;
int i  = foo.get< int >( 2 );
char c = foo.get< char >( 4 );

【问题讨论】:

  • 您编写它的方式是不可能的,因为您的代码没有任何“通用”。您只需要两个成员的选择器。您可以为此使用普通的类设计。对于真正通用的东西,您可以尝试 boost.variant。
  • Kerrek:这只是一个简单的例子。在我的实际程序中,我有更多类型特定的数组。我不想为所有这些编写单独的函数。
  • 所有这些数组都必须是您班级的成员吗?这会很棘手,因为类必须是明确的,但如果你只有有限数量的容器,我觉得模板并不是解决问题的正确领域。
  • 考虑修改这个:template &lt;typename T&gt; struct helper { T arr[10]; T get(std::size_t i) { return arr[i]; } }; struct foo : public helper&lt;int&gt;, public helper&lt;char&gt; {};

标签: c++ template-function


【解决方案1】:

虽然 Jason 提出的解决方案有效,但它远非惯用,而且更难维护,因为 switch 语句 1) 中的 case 值没有明显的含义(“幻数”)和 2) 很容易与switch_value&lt;&gt; 特化中的值不同步。我会建议这样做:

struct Foo {
    Foo() : iArr(), cArr() { }

    template<typename T>
    T get(std::size_t const idx) const     {
        return Foo::get_dispatcher<T>::impl(*this, idx);
    }

private:
    int iArr[10];
    char cArr[10];

    template<typename T>
    struct get_dispatcher;
};

template<>
struct Foo::get_dispatcher<int> {
    static int impl(Foo const& foo, std::size_t const idx) {
        return foo.iArr[idx];
    }
};

template<>
struct Foo::get_dispatcher<char> {
    static char impl(Foo const& foo, std::size_t const idx) {
        return foo.cArr[idx];
    }
};

intchar 以外的任何类型调用Foo::get&lt;&gt; 将产生编译器错误。

【讨论】:

    【解决方案2】:

    您需要添加某种类型的值结构,以便从中获取 switch 语句的值。例如:

    template<typename T>
    struct switch_value {};
    
    template<>
    struct switch_value<int>
    {
        enum { value = 1 };
    };
    
    template<>
    struct switch_value<char>
    {
        enum { value = 2 };
    };
    
    //then inside you structure Foo
    template <typename T>
    T get( int idx )
    {
        switch ( switch_value<T>::value )
        {
        case 1:
            return iArr[ idx ];
        case 2:
            return cArr[ idx ];
        }
    }
    

    这里的好处是,如果您使用没有有效值的类型,这应该会引发编译器错误,因为switch_value&lt;T&gt; 的默认版本没有定义成员value,所以如果您没有专门特定类型的结构,那么这样的模板实例化将失败。

    【讨论】:

    • 杰森:非常感谢!这非常适合我的问题。除非有人提供更优雅的解决方案,否则您的解决方案就是:-)
    【解决方案3】:

    所有这些答案都太过分了。

    template <typename T>
    T get( int idx )
    {
       if ( boost::is_same<T, int>::value)
          return *(T*)&iArr[ idx ];
       else
          return *(T*)&cArr[ idx ];
    }
    

    【讨论】:

    • 如果T 可以是类类型,则这将不起作用,例如std::list&lt;&gt;.
    【解决方案4】:

    你可以专门化成员函数:

    struct foo
    {
        int  iArr[10];
        char cArr[10];
    
        template<typename T>
        T &get(int ipos) { assert( false && "Foo.get: Invalid type!" ); return T(); }
    
        template<>
        int &get<int>(int ipos) { return iArr[ipos]; }
    
        template<> 
        char &get<char>(int ipos) {return cArr[ipos]; }
    
        // add more specialisations for any other types...
    };
    

    适用于 msvc++ 2010。希望这会有所帮助。

    Jason 建议的 switch 专业化也应该可以正常工作。

    【讨论】:

    • 这不是合法的 C++; MSVC 允许它作为静默扩展,但任何其他编译器都会阻塞。
    【解决方案5】:

    对于您的示例来说,这可能有点过头了,但如果您真的一次只需要存储一个数组,那么您可以使用 boost::variant 类和访问者,例如,

    #include <boost/variant.hpp>
    #include <iostream>
    
    template< typename T >
    class GetVisitor : public boost::static_visitor<T>
    {
      public:
        GetVisitor(int index) : index_(index) {};
    
        template <typename U >
        T operator() (U const& vOperand) const
        {
            return vOperand[index_];
        }
    
      private:
        int index_;
    };
    
    
    int main ()
    {
        int  iArr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        char cArr[10] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' };
    
        boost::variant<int*, char*>  intVariant(iArr);   //- assign integer array to variant
        boost::variant<int*, char*>  charVariant(cArr);  //- assign character array to another variant
    
        int  testInt  = boost::apply_visitor(GetVisitor<int>(2),  intVariant);  
        char testChar = boost::apply_visitor(GetVisitor<char>(9), charVariant);
    
        std::cout << "returned integer is "   << testInt  << std::endl;
        std::cout << "returned character is " << testChar << std::endl;
    
        return 0;
    }
    
    output is:   
    returned integer is 3   
    returned character is j   
    

    GetVisitor 类隐含的对变体的限制是该变体的所有成员都必须实现:

    T operator[](int)
    

    因此,您还可以添加例如 std::vector 和 std::deque 作为变体的潜在成员。

    【讨论】:

      【解决方案6】:

      我认为这是您除了只关注模板功能之外想要的:

      在 .h 文件中

      template < typename T >
      struct Foo
      {
          T arr[10];
      
          T get( int idx )
          {
            return arr[ idx ];
          }
      };
      

      你使用它的地方,比如:

      Foo<int> foo1;
      Foo<char> foo2;
      int i  = foo1.get( 2 );
      char c = foo2.get( 4 );
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-16
        • 1970-01-01
        • 2018-04-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多