C ++: Как использовать тип в шаблонной функции для перехода? - PullRequest
9 голосов
/ 23 июня 2011

Я не очень хорошо разбираюсь в шаблонах.Как мне написать функцию шаблона с именем 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 );

Ответы [ 6 ]

9 голосов
/ 23 июня 2011

Хотя решение, предложенное Джейсоном, работает, оно далеко от идиоматического и его сложнее поддерживать, поскольку значения case в выражении switch 1) не имеют очевидного значения («магические числа») и 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];
    }
};

Вызов Foo::get<> с любым типом, кроме int или char, приведет к ошибке компиляции.

8 голосов
/ 23 июня 2011

Вам потребуется добавить структуру значений некоторого типа, которую вы можете использовать для получения значений для вашего оператора 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<T> не определяет член value, так что если вы не специализировались структура для конкретного типа, то такой экземпляр шаблона не удастся.

1 голос
/ 23 июня 2011

Это, вероятно, излишне для вашего примера, но если вам действительно нужно хранить только один массив за раз, тогда вы можете использовать класс boost :: variable и посетителя, например,

#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 в качестве потенциальных членоввариант.

0 голосов
/ 30 июля 2013

Полагаю, это то, что вам нужно, помимо сосредоточения внимания на функции шаблона:

в файле .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 );
0 голосов
/ 30 июля 2013

Все эти ответы являются излишним.

template <typename T>
T get( int idx )
{
   if ( boost::is_same<T, int>::value)
      return *(T*)&iArr[ idx ];
   else
      return *(T*)&cArr[ idx ];
}
0 голосов
/ 23 июня 2011

Вы можете специализировать функцию-член:

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. Надеюсь, это поможет.

Специализация переключателя, предложенная Джейсоном, также должна работать нормально.

...