Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Тип MyString, в котором нет difference_type, при подстановке вызовет ошибку: функция возвращала бы несуществующий тип. Аналогично, конструкция val1 — val2 требует наличия бинарного оператора «минус» и тоже может породить синтаксическую ошибку. Вызов difference с аргументами типа MyString сможет «увидеть» только int-версию функции. Эта единственная версия окажется достаточно подходящей только если в MyString определён оператор преобразования в число. Получается, что шаблонная функция difference проверяет тип аргумента на одновременное выполнение сразу трёх условий: наличие difference_type, наличие оператора вычитания и возможность приведения результата вычитания к типу difference_type (преобразование подразумевается оператором return). Типам, нарушающим хотя бы одно условие, эта перегрузка не видна.
template <typename T>
auto difference(T&& x, T&& y) -> decltype(x - y) { return x - y; }
template <typename T>
auto difference(T&& x, T&& y) -> decltype(std::forward<T>(x) - std::forward<T>(y)) {
return std::forward<T>(x) - std::forward<T>(y);
}
int foo(int) conststruct foo_detector
{
static auto check(void*) -> void; // раз уж С++11, напишем в постфиксной форме - так красивее и однообразнее :)
template<class T>
static auto check(T* p) -> decltype(p->foo(42))*; // указатель на чего бы то ни было отличается от не-указателя
typedef void* match_type; // но мы ожидаем конкретно void foo(int-compatible), поэтому проверяем на void*
};
// менее громоздкий способ писать метафункции - это наследоваться от готовых
template<class T> struct has_foo : std::is_same<foo_detector::match_type, decltype(foo_detector::check((T*)nullptr))> {};
struct P { void foo(int); };
struct Q0 { };
struct Q1 { void foo(void); };
struct Q2 { int foo(void); };
struct Q3 { int foo(int ); };
int main()
{
std::cout << has_foo<P >::value << std::endl;
std::cout << has_foo<Q0>::value << std::endl;
std::cout << has_foo<Q1>::value << std::endl;
std::cout << has_foo<Q2>::value << std::endl;
std::cout << has_foo<Q3>::value << std::endl;
}
!is_same<default_type, decltype(detect(.....))>::value, где default_type — тип результата ловушки default_type detect(...).template<typename T> struct has_foo
{
private: // Спрячем от пользователя детали реализации.
template <typename U, void(U::*pfn)(int) = &U::foo>
struct detector {char _[2];};
static char detect(...); // Статическую функцию и вызывать проще.
template<typename U> static detector<U> detect(U*);
public:
static const bool value = sizeof(detect(static_cast<T*>(0))) != sizeof(char); // Вот видите, готово.
};
foo(unsigned long) и foo(float) требованиям к функции foo, зависит от целей проверки; я исходил из предположения, что они достаточно подходящие. sizeof менее выразителен, чем прямое сравнение «проброшенного» через detect возвращаемого типа с эталоном, хотя и не менее действенен.T* data();. А для deque и прочих делаем обычный вариант с итераторами.deque итераторы того же класса RandomAccess, что и у вектора, а вот сами данные уже не единым куском лежат. И если оптимизация рассчитана именно на непрерывность, а не на произвольный доступ, то как тут быть?vector или подобного контейнера будут использоваться оптимизированные варианты процедур, а с другими контейнерами — обычные. И выбор осуществляется автоматически, пользователю об этом даже не надо задумываться. Ему не надо выбирать между шаблонами MyThingOptimized и MyThingGeneric, не надо подставлять лишние булевы константы в параметры… Он указывает, каким контейнером хочет пользоваться (и то только если ему реально нужен не тот, что по умолчанию), а шаблон MyThing сам приспособится. И Питон с рунтаймом тут вообще не при делах.
SFINAE — это просто