C++:如何使用模板函数中的类型进行分支?

发布于 2024-11-16 15:39:37 字数 549 浏览 0 评论 0原文

我对模板不太熟悉。如何编写名为 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 );

I am not quite proficient with templates. How do I write the a template function called get that chooses the array it gets from based on the template type? See the example below:

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 );

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

碍人泪离人颜 2024-11-23 15:39:37

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

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<> 将产生编译器错误。

While the solution proposed by Jason works, it's far from idiomatic, and is harder to maintain since the case values in the switch statement 1) have no apparent meaning ("magic numbers") and 2) could easily get out of sync with the values in the switch_value<> specializations. I would propose this instead:

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

Invoking Foo::get<> with any type other than int or char will yield a compiler error.

岁月蹉跎了容颜 2024-11-23 15:39:37

您需要添加某种类型的值结构,您可以使用它来获取 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 的默认版本没有定义成员 value,因此如果您没有专门针对特定类型的结构,那么这样的模板实例化将会失败。

You would need to add a value structure of some type you can use to get the values for your switch-statement from. For instance:

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

The nice thing here is this should throw a compiler error if you use a type that does not have a valid value since the default version of switch_value<T> does not define a member value, so if you haven't specialized the structure for a specific type, then such a template instantiation will fail.

行至春深 2024-11-23 15:39:37

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

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

All of these answers are wayyyy overkill.

template <typename T>
T get( int idx )
{
   if ( boost::is_same<T, int>::value)
      return *(T*)&iArr[ idx ];
   else
      return *(T*)&cArr[ idx ];
}
独自←快乐 2024-11-23 15:39:37

您可以专门化成员函数:

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

Works with msvc++ 2010。希望这有帮助。

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

You could specialise the member function:

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

Works with msvc++ 2010. Hope this helps.

The switch specialisation suggested by Jason should also work fine.

静待花开 2024-11-23 15:39:37

对于您的示例来说,这可能有点过分了,但如果您确实一次只需要存储一个数组,那么您可以使用 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 作为变体的潜在成员。

This is probably overkill for your example, but if you really only need to store one array at a time, then you can use the boost::variant class and a visitor, e.g.,

#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   

The restriction on the variant implied by the GetVisitor class is that all members of the variant must implement:

T operator[](int)

So you could also add, e.g., std::vector and std::deque as potential members of the variant.

遇见了你 2024-11-23 15:39:37

我认为除了只关注模板函数之外,这就是您想要的:

在 .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 );

I assume this is what you want beside just focusing on template function:

in a .h file

template < typename T >
struct Foo
{
    T arr[10];

    T get( int idx )
    {
      return arr[ idx ];
    }
};

somewhere you use it like:

Foo<int> foo1;
Foo<char> foo2;
int i  = foo1.get( 2 );
char c = foo2.get( 4 );
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文