[C++] шаблоны: помогите разобраться.
#include <iostream>
using std::cout;
template <typename T, unsigned char Dim>
class Vector
{
public:
T coord[Dim];
///A little hack in order to determine which container is used for representation
class IsFloat {
class No {};
class Yes { No no[2]; };
static Yes Test(float*);
static No Test(...);
public:
enum {check = sizeof(Test(static_cast<T*>(0 == sizeof(Yes)};
};
void f {
if (IsFloat::check) {
cout << "I've been instantinated with float type, let's do wonders\n";
} else {
cout << "General function\n";
}
}
};
int main
{
Vector<double, 20> b;
b.f;
Vector<float, 10> a;
a.f;
return 0;
}
Как писал Страус (который, как известно, Труп) все методы класса-шаблона автоматом являются функциями-шаблонами. Поэтому унаследоваться от Vector так, чтобы осталась зависимость от параметра для класса, но исчезла зависимость от параметра для одного метода f, нельзя. Придется специализировать весь класс...
И главное, по ходу, вся проверка происходит на этапе компиляции, и более того, условие в функции будет выглядеть наверное так: if (true) {...} else {...} или if (false) {...} else {...} и скорее всего вообще соптимизируется компилятором, т.ч. не будет никаких потерь...
А почему размер класса без данных равен 1?

if (boost::is_same<float,T>::value) { блабла }
еще касаемо вопроса: может стоит вообще вынести функцию из класса, сделать ее шаблоном, чтоб она принимала указатель или ссылку на A и уже специализировать для float-а?
А не, зачем. Я просто не додумался, что можно тип проверить на этапе компиляции таким способом, и раз никаких накладных расходов это не несет, то довольно удобно в принципе.
Просто даже если тебе нужна только специализация для float-а, функция будет выглядеть (имхо) некрасиво. А если в случае чего добавится еще несколько типов, то if else if else - будет довольно убого. Пох.
boost/type_traits/is_same.hpp
template <typename T>
::boost::type_traits::yes_type
BOOST_TT_DECL is_same_tester(T*, T*);
::boost::type_traits::no_type
BOOST_TT_DECL is_same_tester(...);
template <typename T, typename U>
struct is_same_impl
{
static T t;
static U u;
BOOST_STATIC_CONSTANT(bool, value =
(::boost::type_traits::ice_and<
(sizeof(type_traits::yes_type) == sizeof(detail::is_same_tester(&t,&u
(::boost::is_reference<T>::value == ::boost::is_reference<U>::value
(sizeof(T) == sizeof(U
>::value;
};
BOOST_TT_AUX_BOOL_TRAIT_DEF2(is_same,T,U::boost::detail::is_same_impl<T,U>::value
но там есть и другая версия, использующая специализацию, не понял как работает - куча макросов, ну зато задумался)
С бустом таже проблема)
А почему размер класса без данных равен 1?
А хз, в принципе это и не важно, важно, что размеры
class No {};
class Yes { No no[2]; };
уж точно различны, сколькоб не было в пустом.
Раз вся проверка происходит на этапе компиляции, то
гипотеза: теперь можно зафигачить сколько угодно "специализаций" функции f добавив хитрый необязательный параметр. Чёт не пойму как это сделать)
Раз вся проверка происходит на этапе компиляции, то гипотеза: теперь можно зафигачить сколько угодно "специализаций" функции f добавив хитрый необязательный параметр. Чёт не пойму как это сделать)Типа такого что-ли:
template <typename spec_type = T>
void f
{
if (boost::is_same<float, spec_type) { ... }
else if (boost::is_same<int, spec_type) { ... }
else { common code }
}
template <class T, class U> struct is_same
{
static const bool value = false;
};
template <class T> struct is_same<T,T>
{
static const bool value = true;
}
в бусте собсно под кучей макросов спрятан этот принцип
если взять например массив A[100] и вычислить выражение &a[10] - &a[5]
в случае с классом размером >0 байт это будет равно разности указателей на элементы поделенное на размер класса
теперь если все классы пустые, то и все &a[i] будут указывать в одно место и соответственно разность не сосчитаешь
это одна из кучи причин
пример вспомнился из одной книжки
template <class T, class U> struct is_same
{
static const bool value = false;
};
template <class T, class T> struct is_same
{
static const bool value = true;
}
не компилируется, ну и правильно, тут всякие редефинишины. Наверное, ты это имел ввиду?
template <class T1, class U> struct is_same1
{
static const bool value = false;
};
template <typename T1> struct is_same1<T1,T1>
{
static const bool value = true;
};
В натуре прикольно, пашет.
Как сделать несколько разных функций f например для инта, дабла и флота полностью в компайл тайм?
да-да, я нагнал...
#include <iostream>
using std::cout;
template <typename T, unsigned char Dim>
class Vector
{
T coord[Dim];
template <class T1, class U> struct is_same1
{
static const bool value = false;
};
template <typename T1> struct is_same1<T1,T1>
{
static const bool value = true;
};
enum _wt {GENERAL, INT, FLOAT};
static const _wt _tmp = (is_same1<T, int>::value ? INT : (is_same1<T, float>::value ? FLOAT : GENERAL;
template<int selector> void g {cout << "General function\n";}
template<> void g<INT> {cout << "Fuction for int\n";}
template<> void g<FLOAT> {cout << "Function for float\n";}
public:
void f { g<_tmp> }
};
int main
{
Vector<int, 20> b;
b.f;
Vector<float, 10> a;
a.f;
Vector<double, 30> c;
c.f;
return 0;
}
По идее, хочется его унаследовать от общего класса, и изменить только одну эту функцию.В принципе, можно сделать так:
template <typename T, unsigned Dim>
class VectorImpl
{
public:
T coord[Dim];
// other generic stuff
};
//
template <typename T, unsigned Dim>
class Vector : public VectorImpl<T, Dim>
{
public:
void f { std::cout << "Generic f" << std::endl; };
};
//
template <unsigned Dim>
class Vector<float, Dim> : public VectorImpl<float, Dim>
{
public:
void f { std::cout << "Vector<float> f" << std::endl; };
};
Но в более сложном случае наследование уже плохо покатит.
Пусть, к примеру, я хочу, чтобы для всех целых типов (параметров шаблона) результат фунции был int, а для целочисленных double?
ReturnType Method{}

Не совсем так, но мысль мою толкнул =)
имея под рукой раннее написанную библиотечку, можно и прямо так писать.

template <class T, class U>
struct max_type
{
enum type_type {GENERAL, INTEGRAL, BIG_INTEGRAL, FLOATING_POINT};
static const type_type t_type = (boost::is_integral<T>::value)?
INTEGRAL : (boost::is_same<T, int64_t>::value || boost::is_same<T, uint64_t>::value)?
BIG_INTEGRAL : (boost::is_floating_point<T>::value)?
FLOATING_POINT : GENERAL;
static const type_type u_type = (boost::is_integral<U>::value)?
INTEGRAL : (boost::is_same<U, int64_t>::value || boost::is_same<U, uint64_t>::value)?
BIG_INTEGRAL : (boost::is_floating_point<U>::value)?
FLOATING_POINT : GENERAL;
template <type_type selector1, type_type selector2>
struct Result
{
typedef T type;
};
template<type_type selector2>
struct Result<FLOATING_POINT, selector2>
{
typedef double type;
};
template<type_type selector2>
struct Result<selector2, FLOATING_POINT>
{
typedef double type;
};
template<>
struct Result<INTEGRAL, BIG_INTEGRAL>
{
typedef U type;
};
typedef typename Result<t_type, u_type>::type type;
};
template <class T1, unsigned char Dim1, class T2 unsigned char Dim2>
typename max_Type<T1, T2>::type scalarMult(const Vector<T1, Dim1>& v1, copnst Vector<T2, Dim2>& v2)
{
...
}
Или смотрел код буста?
Гляди, то что ты хочешь сделать, проще (имхо) можно сделать так.
#include <boost/mpl/if.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/is_floating_point.hpp>
#include <typeinfo>
#include <iostream>
using std::cout;
template <typename T, unsigned char Dim> class Vector {
};
template <class T, class U> struct max_type {
enum type_name {GENERAL, INTEGRAL, BIG_INTEGRAL, FLOATING_POINT};
static const type_name t_type = (boost::is_integral<T>::value) ? INTEGRAL :
(boost::is_same<T, __int64>::value)? BIG_INTEGRAL :
(boost::is_floating_point<T>::value)? FLOATING_POINT :
GENERAL;
static const type_name u_type = (boost::is_integral<U>::value) ? INTEGRAL :
(boost::is_same<U, __int64>::value) ? BIG_INTEGRAL :
(boost::is_floating_point<U>::value)? FLOATING_POINT :
GENERAL;
static const bool _max = t_type > u_type;
public:
typedef typename boost::mpl::if_c< _max, typename T, typename U>::type type;
};
template <typename T1, unsigned char Dim1, typename T2, unsigned char Dim2>
typename max_type<T1, T2>::type
scalarMult(const Vector<T1, Dim1>& v1, const Vector<T2, Dim2>& v2)
{
max_type<T1, T2>::type _result(0);
cout << typeid(_result).name << std::endl;
return _result;
}
int main
{
Vector<int, 20> a;
Vector<unsigned, 30> b;
scalarMult(a, b);
return 0;
}
P. S. Говори, для чего вообще всё это нужно, и скорей всего найдём библиотеку буста где это уже всё сделано

А про boost:

Нужно для написания небольшой шаблонной библиотечки для работы с векторами и матрицами.
Скажем, если ищется скалярное произведение двух векторов интегрального типа, то логично представить его в интегральном типе, если хотя бы один из сомножителей с плавающей точкой - то с плавающей точкой.
Глядя на подобные вещи начинаешь задумываться о применении ML вместо C++
typedef typename boost:Не, это не то. Скажем, из <char, int> я получу int, а из <int, char> char.:if_c< _max, typename T, typename U>::type type;
Так?
static const bool _max = sizeof(T) > sizeof(U) || t_type > u_type;
Не, я хочу именно то, что написал - для интегральных типов получать int, для остальных double.
Оставить комментарий
erotic
Имеется шаблон класса вектора:И мне надо специализировать функцию f в классе, у которого T = float. Насколько я понимаю, раз сама функция не шаблонная, то я не могу именно ее специализировать, и вот так тоже не могу:
на такую специализацию компилятор ругается.
Выходит, что надо специализировать весь класс, у которого T = float. По идее, хочется его унаследовать от общего класса, и изменить только одну эту функцию. Но написать
тоже не получится - выходит очевидная глупость.
Есть какой-нибуд способ сделать специализацию попроще, не прибегая к помощи разнообразных классов-предков Vector?