[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 (true) {...} else {...} или if (false) {...} else {...} и скорее всего вообще соптимизируется компилятором, т.ч. не будет никаких потерь...
А почему размер класса без данных равен 1?
этот грязный хак можно было бы по-красивее сделать с помощью boost-a 
if (boost::is_same<float,T>::value) { блабла }
еще касаемо вопроса: может стоит вообще вынести функцию из класса, сделать ее шаблоном, чтоб она принимала указатель или ссылку на A и уже специализировать для float-а?

if (boost::is_same<float,T>::value) { блабла }
еще касаемо вопроса: может стоит вообще вынести функцию из класса, сделать ее шаблоном, чтоб она принимала указатель или ссылку на A и уже специализировать для float-а?
А не, зачем. Я просто не додумался, что можно тип проверить на этапе компиляции таким способом, и раз никаких накладных расходов это не несет, то довольно удобно в принципе.
Просто даже если тебе нужна только специализация для float-а, функция будет выглядеть (имхо) некрасиво. А если в случае чего добавится еще несколько типов, то if else if else - будет довольно убого. Пох.
Посмотрел как сделано у буста, "no-partial-specialization" version вроде таже идея:
boost/type_traits/is_same.hpp
но там есть и другая версия, использующая специализацию, не понял как работает - куча макросов, ну зато задумался)
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;
}
в бусте собсно под кучей макросов спрятан этот принцип
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] будут указывать в одно место и соответственно разность не сосчитаешь
это одна из кучи причин
пример вспомнился из одной книжки
если взять например массив A[100] и вычислить выражение &a[10] - &a[5]
в случае с классом размером >0 байт это будет равно разности указателей на элементы поделенное на размер класса
теперь если все классы пустые, то и все &a[i] будут указывать в одно место и соответственно разность не сосчитаешь
это одна из кучи причин
пример вспомнился из одной книжки
Так как ты написал
не компилируется, ну и правильно, тут всякие редефинишины. Наверное, ты это имел ввиду?
В натуре прикольно, пашет.
Как сделать несколько разных функций f например для инта, дабла и флота полностью в компайл тайм?
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?
Пусть, к примеру, я хочу, чтобы для всех целых типов (параметров шаблона) результат фунции был int, а для целочисленных double?
typedef ReturnType if<.., int, float>::value;
ReturnType Method{}
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)
{
...
}
Ты сам понял как юзать селекторы для выведения типов или всё таки чтото читал?
Или смотрел код буста?
Гляди, то что ты хочешь сделать, проще (имхо) можно сделать так.
P. S. Говори, для чего вообще всё это нужно, и скорей всего найдём библиотеку буста где это уже всё сделано
Или смотрел код буста?
Гляди, то что ты хочешь сделать, проще (имхо) можно сделать так.
#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:
:if_c не знал.
Нужно для написания небольшой шаблонной библиотечки для работы с векторами и матрицами.
Скажем, если ищется скалярное произведение двух векторов интегрального типа, то логично представить его в интегральном типе, если хотя бы один из сомножителей с плавающей точкой - то с плавающей точкой.
А про boost:
:if_c не знал.Нужно для написания небольшой шаблонной библиотечки для работы с векторами и матрицами.
Скажем, если ищется скалярное произведение двух векторов интегрального типа, то логично представить его в интегральном типе, если хотя бы один из сомножителей с плавающей точкой - то с плавающей точкой.
Попробуй посмотреть эти библиотеки
Глядя на подобные вещи начинаешь задумываться о применении 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;
Так?
static const bool _max = sizeof(T) > sizeof(U) || t_type > u_type;
Не, я хочу именно то, что написал - для интегральных типов получать int, для остальных double.
Оставить комментарий
erotic
Имеется шаблон класса вектора:И мне надо специализировать функцию f в классе, у которого T = float. Насколько я понимаю, раз сама функция не шаблонная, то я не могу именно ее специализировать, и вот так тоже не могу:
на такую специализацию компилятор ругается.
Выходит, что надо специализировать весь класс, у которого T = float. По идее, хочется его унаследовать от общего класса, и изменить только одну эту функцию. Но написать
тоже не получится - выходит очевидная глупость.
Есть какой-нибуд способ сделать специализацию попроще, не прибегая к помощи разнообразных классов-предков Vector?