2016-04-15 5 views
4

Рассмотрим следующий ряд частичных специализаций:Улучшенный шаблон для цепочки приоритетов частичной специализации?

template <typename T, typename Enable=void> 
struct foo { 
    void operator()() const { cout << "unspecialized" << endl; } 
}; 

template <typename T> 
struct foo<T, enable_if_t< 
    is_integral<T>::value 
>>{ 
    void operator()() const { cout << "is_integral" << endl; } 
}; 

template <typename T> 
struct foo<T, enable_if_t< 
    sizeof(T) == 4 
    and not is_integral<T>::value 
>>{ 
    void operator()() const { cout << "size 4" << endl; } 
}; 

template <typename T> 
struct foo<T, enable_if_t< 
    is_fundamental<T>::value 
    and not (sizeof(T) == 4) 
    and not is_integral<T>::value 
>>{ 
    void operator()() const { cout << "fundamental" << endl; } 
}; 

// etc... 

Live Demo

Я вижу, что такого рода вещи все время (на самом деле, another StackOverflow answer elsewhere дает один и тот же шаблон для подобной задачи). Хотя это и работает, этот код имеет некоторые серьезные проблемы с ремонтопригодностью, а также исключает, например, частичные специализации пользовательского уровня с более высоким приоритетом, если вышеуказанный код находится в библиотеке. Что лучше для выражения этой идеи? Я чувствую, что должно быть что-то (может быть, связано с наследованием и параметрами вариационного шаблона?), Которые могут выразить эту идею более чисто и сдержанно. (Предположим также, что каждая из специализаций является полным классом, а не просто функтором, поэтому перегруженные функции не работают упрощенным способом).

ответ

2

Почему я отвечая на мой собственный вопрос

Так что я прослушивается этим до сих пор задают этот вопрос, и я никогда не был полностью удовлетворен оригинальный ответ. После долгих ворчаний и проб/ошибок, я придумал шаблон, который мне намного счастливее, который использует отправку тегов. Является ли это на самом деле лучше, более читабельным и более удобным, чем предыдущий, для вас судить, но мне это нравится лучше. Не стесняйтесь разобрать его, критиковать и разбить. :-)

Базовая версия

Без дальнейших церемоний, вот код, который решить простейший вариант задачи

template <typename> struct always_true : true_type { }; 
template <typename> struct always_false : false_type { }; 

template <typename T, template <class...> class condition=always_false, 
    typename flag=integral_constant<bool, condition<T>::value> 
> 
struct foo; 

//////////////////////////////////////// 
// "unspecialized" version 

// put always_true and false_type together here so that no one gets here accidentally 
template <typename T, typename true_or_false_type> 
struct foo<T, always_true, true_or_false_type> { 
    void operator()() const { cout << "unspecialized" << endl; } 
}; 

//////////////////////////////////////// 
// is_fundamental 

template <typename T> 
struct foo<T, is_fundamental, true_type> { 
    void operator()() const { cout << "is_fundamental" << endl; } 
}; 
template <typename T> struct foo<T, is_fundamental, false_type> : foo<T, always_true> { }; 

//////////////////////////////////////// 
// is_integral 

template <typename T> 
struct foo<T, is_integral, true_type> { 
    void operator()() const { cout << "is_integral" << endl; } 
}; 
template <typename T> 
struct foo<T, is_integral, false_type> : foo<T, is_fundamental> { }; 

//////////////////////////////////////// 
// sizeof(T) == 4 

template <typename T> 
using size_is_4 = integral_constant<bool, sizeof(T) == 4>; 

template <typename T> 
struct foo<T, size_is_4, true_type> { 
    void operator()() const { cout << "size_is_4" << endl; } 
}; 
template <typename T> 
struct foo<T, size_is_4, false_type> : foo<T, is_integral> { }; 

//////////////////////////////////////// 
// Now put the most specialized condition in the base of this template 

template <typename T, typename true_or_false_type> 
struct foo<T, always_false, true_or_false_type> : foo<T, size_is_4> { }; 

Цепь старшинства, состоявшейся в хелперов структуры в предыдущем ответе , кодируется в наследовании.

Больше навороты

Добавление возможность включать пользовательские частичные специализации с более высоким приоритетом, чем библиотеки из них занимает немного больше делать, но принцип тот же. Полная версия в этом demo.

+0

Ваше решение интересно, вы также можете взглянуть на подход в моем редактировании. Он использует специализацию и тип отправки тегов ... –

+1

@WojciechFrohmberg Спасибо. Я думаю, что полезно иметь оба подхода здесь, независимо от того, какой из них «лучше» (для некоторого определения лучше). Спасибо за ваше время и усилия, чтобы внести свой вклад в этот ответ –

3

Разрастание графа состояния может быть решена с помощью вспомогательных структур:

#include <iostream> 
#include <type_traits> 

using namespace std; 

template <bool ThisCondition, class ParentCondition = void, class = void> 
struct condition_resolver { 
    static constexpr bool is_condition_resolver = true; 
    static constexpr bool parent_condition_v = !ThisCondition; 
    static constexpr bool value = ThisCondition; 
}; 

template <bool ThisCondition, class ParentCondition> 
struct condition_resolver<ThisCondition, ParentCondition, enable_if_t<ParentCondition::is_condition_resolver> > { 
    static constexpr bool is_condition_resolver = true; 
    static constexpr bool parent_condition_v = !ThisCondition && ParentCondition::parent_condition_v; 
    static constexpr bool value = ThisCondition && ParentCondition::parent_condition_v; 
}; 

template <typename T, typename Enable=void> 
struct foo { 
    void operator()() const { cout << "unspecialized" << endl; } 
}; 

template <typename T> 
struct is_integral_foo: condition_resolver<is_integral<T>::value> { }; 

template <typename T> 
struct foo<T, enable_if_t<is_integral_foo<T>::value>>{ 
    void operator()() const { cout << "is_integral" << endl; } 
}; 

template <typename T> 
struct has_size_four_foo: condition_resolver<sizeof(T) == 4, is_integral_foo<T>> { }; 

template <typename T> 
struct foo<T, enable_if_t< has_size_four_foo<T>::value>>{ 
    void operator()() const { cout << "size 4" << endl; } 
}; 

template <typename T> 
struct is_fundamental_foo: condition_resolver<is_fundamental<T>::value, has_size_four_foo<T>> { }; 

template <typename T> 
struct foo<T, enable_if_t<is_fundamental_foo<T>::value>>{ 
    void operator()() const { cout << "fundamental" << endl; } 
}; 

typedef char four_sized[4]; 

int main() { 
    foo<int>()(); 
    foo<four_sized>()(); 
    foo<nullptr_t>()(); 
} 

Выход:

is_integral 
size 4 
fundamental 

PS. Имейте в виду, что void, который также является основным заставит компилятор производить предупреждение, что sizeof(void) считается ...

Edit:

Если вам действительно нужно использовать специализацию для решения разрастание проблемы состояния это может заинтересовать:

#include <iostream> 
#include <type_traits> 

using namespace std; 

template <class Tag, int Level, class... Args> 
struct concrete_condition_resolver; 


template <class Tag, int Level, class... Args> 
struct condition_resolver; 

template <class ConditionResolver> 
struct condition_resolver_parent { 
    template<class CR = ConditionResolver> 
    constexpr enable_if_t<CR::level != 0, bool> operator()(bool parent) { 
     return (!parent && static_cast<const ConditionResolver*>(this)->condition && typename ConditionResolver::LevelUp()(true)) || 
      (parent && !static_cast<const ConditionResolver*>(this)->condition && typename ConditionResolver::LevelUp()(true)); 
    } 

    template<class CR = ConditionResolver> 
    constexpr enable_if_t<CR::level == 0, bool> operator()(bool parent) { 
     return (!parent && static_cast<const ConditionResolver*>(this)->condition) || 
      (parent && !static_cast<const ConditionResolver*>(this)->condition); 
    } 
}; 

template <class Tag, int Level, class... Args> 
struct condition_resolver: concrete_condition_resolver<Tag, Level, Args...>, condition_resolver_parent<condition_resolver<Tag, Level, Args...>> { 
    using LevelUp = condition_resolver<Tag, Level - 1, Args...>; 
    using tag = Tag; 
    static constexpr int level = Level; 
    constexpr condition_resolver() {} 
}; 


struct foo_tag { }; 

template <class First, class... Args> 
struct concrete_condition_resolver<foo_tag, 0, First, Args...> { 
    static constexpr bool condition = is_integral<First>::value; 
}; 

template <class First, class... Args> 
struct concrete_condition_resolver<foo_tag, 1, First, Args...> { 
    static constexpr bool condition = sizeof(First) == 4; 
}; 

template <class First, class... Args> 
struct concrete_condition_resolver<foo_tag, 2, First, Args...> { 
    static constexpr bool condition = is_fundamental<First>::value; 
}; 

template <typename T, typename = void> 
struct foo; 

template <typename T> 
struct foo<T, enable_if_t<condition_resolver<foo_tag, 0, T>()(false)>>{ 
    void operator()() const { cout << "is_integral" << endl; } 
}; 

template <typename T> 
struct foo<T, enable_if_t<condition_resolver<foo_tag, 1, T>()(false)>>{ 
    void operator()() const { cout << "size 4" << endl; } 
}; 

template <typename T> 
struct foo<T, enable_if_t<condition_resolver<foo_tag, 2, T>()(false)>>{ 
    void operator()() const { cout << "is_fundamental" << endl; } 
}; 


typedef char four_sized[4]; 

int main() { 
    foo<int>()(); 
    foo<four_sized>()(); 
    foo<nullptr_t>()(); 
} 

Этот подход применим даже для функций перегрузки с использованием enable_if, в то время как частичная специализация имеет дело только с ...

структур
+0

Похож на довольно аккуратный подход к управлению условиями. Я собирался предложить что-то подобное, но мой был бы немного неуклюже, чем ваш.+1 – Zuu

+0

Как это лучше, чем что-то гораздо проще, чем 'template using condition1_t = std :: integ_constant :: value>;' (и затем определяя 'condition_2' аналогично, но с ссылкой на 'condition_1' вместо' condition_0') –

+0

@DavidHollman Ну, на самом деле вы не можете сделать это, используя только одно использование, потому что оно содержит не отрицаемое частичное значение для последнего условия ... например 'condition1_t' содержит' sizeof (T) == 4', и если он должен быть корректным для использования в 'condition2_t', он должен быть' sizeof (T)! = 4' ... –

Смежные вопросы