2016-08-09 7 views
3

Я пытаюсь специализировать шаблон структуры для нескольких типов одновременно, используя SFINAE. Я знаю, что-то вроде следующих работ:Использование частичной специализации SFINAE без касания основного шаблона

#include <iostream> 

template <typename T, typename Enable = void> 
struct S { 
    void operator()() { 
     std::cout << "Instantiated generic case" << std::endl; 
    } 
}; 

template<typename T> 
using enabled_type = typename std::enable_if< 
         std::is_same<T, int>::value || 
         std::is_same<T, float>::value 
        >::type; 

template <typename T> 
struct S<T, enabled_type<T>> { 
    void operator()() { 
     std::cout << "Instantiated int/float case" << std::endl; 
    } 
}; 

int main() { 
    S<float>()(); 
    return 0; 
} 

Моя проблема заключается в том, что я не могу изменить основной шаблон в S структуры добавить typename Enable = void, как часть внешнего заголовка только для библиотеки. Таким образом, основной шаблон будет выглядеть следующим образом:

template <typename T> 
struct S { 
    void operator()() { 
     std::cout << "Instantiated generic case" << std::endl; 
    } 
}; 

Есть ли способ, что я все еще мог использовать SFINAE специализироваться этот шаблон?

Edit: Обратите внимание, что S структура используется код во внешней библиотеке, так что я есть на самом деле специализируются S и не может его подкласса. Кроме того, реальный код, над которым я работаю, намного сложнее и выиграет от SFINAE намного больше, чем этот простой пример (у меня есть несколько параметров шаблона, которые должны быть специализированы для всех комбинаций нескольких типов).

+0

В зависимости от того, какие фактические формы 'T' вас интересуют, это может помочь: https://stackoverflow.com/a/30991097/4326278 – bogdan

ответ

2

у меня есть плохие новости для вас: то, что вы хотите, это невозможно. Если первичный шаблон не имеет дополнительного параметра шаблона, который по умолчанию равен void с целью выполнения enable_if, просто невозможно сделать это в самом общем смысле. Ближайший вы можете прийти, чтобы специализировать-структуру для самого шаблона класса, другими словами:

template <class T> 
struct foo {}; 

template <class T> 
struct S<foo<T>> {}; 

Это будет работать. Но, очевидно, это не дает той же гибкости, что и специализация, если она соответствует признаку.

Ваша проблема на самом деле в точности эквивалентна задаче специализации std::hash для любых типов, удовлетворяющих признаку. Как и в вашей проблеме, определение шаблона основного класса не может быть изменено, так как оно находится в библиотечном коде, и в некоторых случаях код библиотеки фактически использует специализации hash, поэтому невозможно определить класс нового шаблона.

Вы можете увидеть аналогичный вопрос здесь: Specializing std::hash to derived classes. Наследование от класса - хороший пример того, что может быть выражено как признак, но не как шаблонный класс. У некоторых хорошо осведомленных людей C++ были глаза на этот вопрос, и никто не дал лучшего ответа, чем решение OP о написании макроса, чтобы автоматически выдавать специализацию. Я думаю, что это тоже будет лучшим решением для вас, к сожалению.

В ретроспективе hash, возможно, должен был быть объявлен вторым параметром шаблона, который по умолчанию был недействительным для поддержки этого прецедента с минимальными накладными расходами в других случаях.Я мог бы поклясться, что даже видел об этом где-то, но я не могу отследить его. В случае кода вашей библиотеки вы можете попытаться указать проблему, чтобы изменить ее. Это, по-видимому, не является нарушением изменений, то есть:

template <class T, class = void> 
struct S {}; 

template <> 
struct S<double> {}; 

представляется действительным.

+0

Большое спасибо за объяснение. Кажется, что макросы - это путь для моей конкретной проблемы, но интересная общая проблема специализации с использованием признака, похоже, не имеет решения. – ibab

2

Вы уверены, что простой старой специализации будет недостаточно?

struct S_int_or_float { 
    void operator()() { 
     std::cout << "Instantiated int/float case" << std::endl; 
    } 
}; 

template<> struct S<int>: S_int_or_float {}; 
template<> struct S<float>: S_int_or_float {}; 

Edit: Вы говорите, что вы должны обеспечить сочетание параметров ... Так что есть по крайней мере два из них ... Давайте посмотрим, что мы можем сделать с этим:

struct ParentS { /* some implementation */ }; 

template <class First, class Second> 
struct S<First, Second, enable_if_t<trait_for_first_and_second<First, Second>::value, first_accepted_type_of_third_parameter> >: ParentS { }; 

template <class First, class Second> 
struct S<First, Second, enable_if_t<trait_for_first_and_second<First, Second>::value, second_accepted_type_of_third_parameter> >: ParentS { }; 

// ... 

это не так хорошо, как если бы дополнительный параметр только для SFINAE, но до сих пор не экспоненциальный ...

+0

Согласно обновленному вопросу (и моему первоначальному подозрению), float_or_int вещь был просто примером. Фактический момент состоит в том, чтобы специализировать структуру для всех типов, соответствующих признаку, поэтому это не отвечает на вопрос. –

+2

Да, специализация int/float - это всего лишь пример. Я начал думать о SFINAE, потому что понял, что это упростит код, помимо того, что я мог бы достичь с помощью макросов. В моем случае у меня есть несколько параметров шаблона, которые должны быть специализированными, а это значит, что есть комбинаторный взрыв. – ibab

+0

Использование отдельной структуры и наследование от нее, когда специализация сокращает много кода, который необходимо будет записать. Используя этот подход, макро-решение выглядит не так уж плохо. Большое спасибо! – ibab