В настоящее время я пишу шаблон, который работает по-разному в зависимости от категории ввода.

Есть 3 случая, которые я хочу добавить в свой класс черт.

A. Тип имеет typedef type_category, используйте его.

B. Тип не имеет typedef, используйте тип regular_tag (наиболее распространенный случай)

C. Моя специализация, std::list<T> использует тип special_tag для любого T.

Как бы я справился с этим? Сделать A и C. или B. и C. просто, но я не знаю, как получить все 3.

РЕДАКТИРОВАТЬ

Пример может облегчить понимание.

class Foo
{
    typedef foo_tag type_category;
}
class Bar;

my_traits<Foo>::type(); // makes a foo_tag
my_traits<Bar>::type(); // makes a regular_tag
my_traits<std::list<Baz>>::type(); // makes a special_tag because I want special list proce

Ssing.

3
Flame 30 Авг 2011 в 00:18

3 ответа

Лучший ответ

Строительные леса могут выглядеть так:

template <typename T>
struct MyTrait
{
  typedef typename MyHelper<T, CheckForType<T>::value>::type tag;
};

template <typename T>
struct MyTrait<std::list<T>>
{
  typedef special_tag tag;
};

Нам нужен помощник:

template <typename T, bool>
struct MyHelper
{
  typedef regular_tag tag;
};
template <typename T>
struct MyHelper<T, true>
{
  typedef typename T::type_category tag;
};

Теперь все, что нам нужно, это свойство типа для проверки определения типа члена:

template<typename T>
struct CheckForType
{
private:
    typedef char                      yes;
    typedef struct { char array[2]; } no;

    template<typename C> static yes test(typename C::type_category*);
    template<typename C> static no  test(...);
public:
    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

Применение:

MyTrait<Foo>::tag
7
Kerrek SB 9 Мар 2016 в 14:29

Код Керрека работает, но следующий код короче и универсальнее:

template <typename T,typename DefaultType>
class MyTrait
{
    template <typename C> static DefaultType               test( ... );
    template <typename C> static typename C::type_category test( typename C::type_category * );
public:
    using type = decltype( test<T>(nullptr) );
};

Причина, по которой я говорю, что код Керрека не универсален, заключается в том, что он требует, чтобы вы жестко запрограммировали тип «special_tag», чтобы ваш класс MyTrait всегда использовал один и тот же тег по умолчанию. Код, который я предоставил, позволит вам использовать класс MyTrait с разными значениями по умолчанию. Например, может случиться так, что где-то в коде вы хотите, чтобы значение по умолчанию было int , если type_category не определено, но в другом месте вы хотите, чтобы это было с плавающей точкой .

Давайте посмотрим на пример. Предположим, мы разрабатываем класс, который должен принимать класс контейнера, такой как стандартная библиотека vector , в качестве параметра шаблона. В нашем классе мы хотим использовать тот же size_type , что и базовый контейнер. Однако может случиться так, что кто-то предоставит нам контейнер, для которого не определен size_type (например, valarray не определяет size_type ). В этом случае давайте представим, что я хочу использовать int по умолчанию (вам, вероятно, следует использовать size_t , как и другие стандартные контейнеры, но если бы я изменил код, вы бы не быть в состоянии сказать, что код действительно работает). Для этого сценария я изменил имя класса с «MyTrait» на «size_typeof», и я изменил «type_category» на «size_type» (поскольку это то, что я хочу найти). Некоторый код для этого сценария вместе с примером основной функции для просмотра типов определенных переменных приведены ниже:

#include <iostream>
#include <vector>
#include <valarray>
#include <typeinfo>

template <typename T,typename DefaultType>
class size_typeof
{
    template <typename C> static DefaultType           test( ... );
    template <typename C> static typename C::size_type test( typename C::size_type * );
public:
    using type = decltype( test<T>(nullptr) );
};

template <typename ContainerType>
class Matrix {
    private:
        // Change int to size_t in real code. 
        using size_type = typename size_typeof<ContainerType,int>::type;
        size_type Rows;
        size_type Cols;
    public:
        Matrix( size_t rows, size_t cols ) : Rows(rows), Cols(cols) {}
        size_type rows(){ return Rows; }
        size_type cols(){ return Cols; }
};

int main()
{
    // Give the matrices some nonzero dimensions
    Matrix<std::vector<double>> vec(5,2);
    Matrix<std::valarray<double>> val(4,3);

    // vectors have a size_type, almost always defined as size_t. 
    // So this should return your compiler's code for size_t 
    // (on my computer, this is "m")
    std::cout << typeid( vec.rows() ).name() << std::endl; 

    // valarrays do not have a size_type. 
    // So this should return your compiler's code for int 
    // (on my computer, this is "i")
    // It should return this code, since we used int as the default  
    // size_type in the Matrix class
    std::cout << typeid( val.rows() ).name() << std::endl; 
    return 0;
}

Ok. Замечательно. Но предположим, что я действительно хотел использовать только жестко заданное значение по умолчанию для DefaultType, вместо того, чтобы указывать DefaultType в качестве параметра шаблона. Угадай, что? Значения параметров шаблона по умолчанию. Просто определите size_typeof как:

template <typename T,typename DefaultType = int>
class size_typeof {
    ...
};

И тебе хорошо идти. Больше не нужно указывать тип по умолчанию "int". Например, в нашем классе Matrix мы могли бы использовать

using size_type = size_typeof<ContainerType>;

Конец.

1
bremen_matt 21 Авг 2015 в 11:42
template<class T> 
T::type_category getType(T t, typename T::type_category c=typename T::type_category()) 
{ return c;}
template<class T> 
regular_tag getType(T t, ...) 
{ return regular_tag();}
template<class T>
special_tag getType(std::list<T> t, typename std::list<T>::type_category c=typename std::list<T>::type_category()) 
{ return special_tag();}

int main() {
    auto a = getType(int);
    auto b = getType(std::iterator_traits<char*>);
    auto c = getType(std::list<char>);
}

Я не очень хорошо разбираюсь в SFINAE, поэтому я сомневаюсь, что это вообще компилируется, но что-то вроде этого - это то, куда нужно смотреть.

0
Mooing Duck 29 Авг 2011 в 20:37