В настоящее время я пишу шаблон, который работает по-разному в зависимости от категории ввода.
Есть 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 ответа
Строительные леса могут выглядеть так:
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
Код Керрека работает, но следующий код короче и универсальнее:
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>;
Конец.
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, поэтому я сомневаюсь, что это вообще компилируется, но что-то вроде этого - это то, куда нужно смотреть.
Похожие вопросы
Новые вопросы
c++
C ++ - это язык программирования общего назначения. Первоначально он был разработан как расширение C и имеет аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде (который должен быть) скомпилирован с помощью компилятора C ++. Используйте тег для конкретной версии для вопросов, связанных с конкретной версией стандарта [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] или [C ++ 23] и т. Д. .