Здравствуйте, rpz, Вы писали:
К>>// функции над типами
rpz>Это немного не то.. Тут предполагается что мы заранее знаем будущий тип, точнее его модификацию(AddStar или RemoveStar). Такие же штуки есть в новой STL. remove_reference и add_reference. А нужно вывести его автоматически.
Это я просто показывал — как могут использоваться функции (в том числе — идентичная функция) в функциях высшего порядка. Не более того.
К>>Во-вторых, мы можем передать в шаблон не сам тип, а его характеристику (traits). И шаблон оттуда извлечёт всё, что ему нужно, в том числе исходный тип.
rpz>Ну а толку, если мы уже передали переменную и передалась она по значению из-за вывода аргументов шаблона. Тут надо заставить менться тип аргумента принимающей функции.
Это если ты пишешь одноходовку. А если какую-нибудь навороченную конструкцию — то может оказаться, что traits передавать/запоминать удобнее.
К>>В-третьих, для нескольких разных типов будет одинаковый ParameterType. Особенно Александреску любил находить "наиболее подходящий способ передачи параметров". Для примитивных типов выгоднее передавать их в функцию по значению: foo(int). Для громоздких типов — по константной ссылке: foo(const std::string). Для неконстантных ссылок — как есть: foo(int&).
К>>Так что TypeTraits<std::string>::ParameterType может быть const std::string&.
rpz>Так идея-то как раз в этом, что по типу передаваемого значения определяется как передавать аргумент. В общем задача такова. Написать что-то типа
rpz>int i;
rpz>A a;
rpz>foo(i);//Вызывается foo(int) **1
rpz>foo(a);//Вызывается foo(const A&) **2
rpz>Но как я понял из вашего объяснения в таком виде задача нерешаема.
Задача как раз решаема, но
1) В некоторых случаях от неё больше вреда, чем пользы. Причём вред вплоть до стрельбы по памяти.
2)
Нужно понять особенности ссылок в С++. Это не просто автоматически разыменованные указатели, как могло бы показаться, а довольно-таки специфическое природное явление.
По обоим пунктам столько слов уже написано, что прямо сейчас я отвлекаться не буду.
Итак, собственно, решение задачи. Возможно, не самое красивое, но уж какое мне пришло в голову в полодинадцатого ночи.
#include <iostream>
#include <typeinfo>
/////////////////////////
// инструменты для работы
#include <type_traits>
#include <boost/mpl/if.hpp>
// предикат, определяющий, какие типы мы будем передавать как есть (т.е. по значению):
// примитивные, указатели и ссылки (явно указанные ссылки будем передавать как ссылки)
template<class T> struct passed_by_value :
std::integral_constant<bool, std::is_fundamental<T>::value | std::is_pointer<T>::value | std::is_reference<T>::value >
{};
// метафункция, возвращающая тип передаваемого аргумента по типу исходных данных
template<class T> struct argument_type : boost::mpl::if_< passed_by_value<T>, T, const T& > {};
//////////////////////////////////////////////////////////////
// бизнес-логика пусть будет здесь (чтобы ниже не повторяться)
template<class T> void foo_impl(T t) { std::cout << __PRETTY_FUNCTION__ << " : " << typeid(T).name() << std::endl; }
int x = 123;
std::string y = "hello";
void test_impl()
{
foo_impl<int>(x);
foo_impl<const std::string&>(y);
}
////////////////////////////////////////////////////////////
// лобовое решение - требует явно указывать параметр шаблона
template<class T> void foo0(typename argument_type<T>::type t) { foo_impl<typename argument_type<T>::type>(t); }
void test0()
{
// ужасно!
foo0<int>(x);
foo0<std::string>(y);
// для макроса сгодится (двойное упоминание аргумента)
foo0<decltype(x)>(x);
foo0<decltype(y)>(y);
}
///////////////////////////////////
// делаем чуть красивее - на SFINAE
template<class T> typename std::enable_if< passed_by_value<T>::value, void>::type foo1(T t) { foo_impl<T> (t); }
template<class T> typename std::enable_if<!passed_by_value<T>::value, void>::type foo1(const T& t) { foo_impl<const T&>(t); }
// плохо тем, что если нам нужна функция от двух и более аргументов, мы получим комбинаторный взрыв SFINAE
void test1()
{
foo1(x);
foo1(y);
}
////////////////////////////////////////////////////////////
// менее красиво, зато легче писать многоаргументные функции
// тип-обёртка для аргумента
template<class T> struct box_t
{
typedef typename argument_type<T>::type type;
type value;
box_t(type v) : value(v) {}
};
// SFINAE здесь для того, чтобы последовательно провести политику партии :)
template<class T> typename std::enable_if< passed_by_value<T>::value, box_t<T> >::type box(T t) { return box_t<T>(t); }
template<class T> typename std::enable_if<!passed_by_value<T>::value, box_t<T> >::type box(const T& t) { return box_t<T>(t); }
template<class T, class U> void foo2(box_t<T> t, box_t<U> u) // никакого SFINAE не потребовалось
{
// не стал выдумывать, что б такого реализовать в этой функции - просто вызову нашу старую добрую дважды
foo_impl<typename box_t<T>::type>(t.value); // вот, кстати: box_t выступил и объектом, и характеристикой типа
foo_impl<typename box_t<U>::type>(u.value);
}
void test2()
{
foo2(box(x), box(y));
}
// проверяем
int main()
{
test_impl();
test0();
test1();
test2();
}