2017-01-08 2 views
4

Я хочу сделать шаблон шаблона в C++ 14. Априори, кажется, что следующий код делает трюкШаблон для шаблона в C++ 14

template<class T> 
struct KeyType {}; 

template<class T> 
struct ValueType { 
    T x; 
}; 

template<template<class> class K> 
struct Map; 

template<> 
struct Map<KeyType> { 
    template<class T> 
    using type = ValueType<T>; 
}; 

ValueType<int> test{42}; 
Map<KeyType>::type<int> testM{42}; // Same as above 

Однако следующее выражение, при компиляции с лязгом ++ v3.8, возвращает ложь.

template<template<class> class TemplateType> 
struct NeedsTemplate; 

std::is_same< 
    NeedsTemplate<ValueType>, 
    NeedsTemplate<Map<KeyType>::type> 
>::value; // False 

Я понимаю, почему это неверно: it has already been answered here. В принципе, стандарт гарантирует, что шаблонные экземпляры псевдонимов должны распознаваться как одно и то же, но ничего не говорит о шаблонах. Следовательно, с g ++, std::is_same истинно, и с clang ++, это неверно.

Мой вопрос: как я могу реализовать шаблон для шаблона, который удовлетворяет требованиям std::is_same как с g ++, так и с clang ++? Я готов использовать макрос как последнее средство ...

ответ

1

Не используйте шаблоны в качестве примитивов метапрограммирования. Используйте типы. Аналогично, избегайте значений.

template<template<class...>class Z> 
struct ztemplate{ 
    template<class...Ts> 
    using apply=Z<Ts...>; 
}; 
template<class Z, class...Ts> 
using apply=typename Z::template apply<Ts...>; 
using zapply=ztemplate<apply>; 

Вы никогда не работать с сырыми template с, всего ztemplate с.

template<class T> 
struct KeyType {}; 
using zKeyType=ztemplate<KeyType>; 

C++ metaprogramming работает гораздо приятнее с типами. Если вы хотите ограничить свои типы (например, он должен быть ztemplate), напишите SFINAE или psuedo-concept, чтобы обеспечить его соблюдение.

В качестве бонуса ztemplate является значением , определяющим шаблон. Это открывает вам метапрограммирование в стиле ханы.

Это означает, что вы должны канонически обернуть свой шаблонный код и вырезать прямые параметры шаблона и значения (заменяя соответственно ztemplate и интегральной константой). Но вы получаете гораздо более мощное метапрограммирование.

Вместо X<Blah> do apply<zX, Blah>.Фактически, apply становится единственным шаблоном, который вы используете напрямую.

Примечание apply<zapply, zX, Blah> и apply<zapply, zapply, zX, Blah> и т. Д. То же самое, что и apply<zX, Blah>.

+0

Очень интересно, спасибо. Это требует некоторой работы, но я думаю, что это может стоить того. Что-то я не понимаю: почему вы вводите тип 'zapply'? Это только пример? – Kevin

+1

@Kevin Ну, вы хотите, чтобы метапрограмма с помощью 'apply' сама, вам нужно это как тип, а не как шаблон. ;) Например, предположим, что у вас есть список ztemplates и отдельный список типов. Сначала вы создаете кросс-произведение двух списков (создавая список списков из 2-х элементов). Затем вы накладываете fmap на элементы перекрестного продукта или что-то подобное. – Yakk

1

То, о чем вы просите, в общем, эквивалентно функциональному сравнению, которое эквивалентно проблеме остановки, т. Е. Не вычислимо. Однако ...

Если вы хотите сделать это только для определенных заранее определенных шаблонов, вы можете специализировать их для класса тегов, а затем вы можете использовать NeedsTemplate в классе тегов. То есть,

namespace Reflect { 
    struct Reflect {}; 
} 

template<class T> 
struct KeyType {}; 

namespace Reflect { 
    struct KeyType {}; 
} 

template<> 
struct KeyType<Reflect::Reflect> { 
    using type = Reflect::KeyType; 
}; 

... и затем работать с KeyType<Reflect::Reflect>, что тип, вместо KeyType, что шаблон. Обратите внимание, что вы все равно можете взять KeyType (шаблон) на интерфейсе; вы просто вызываете std::is_same<> на размышления. Также обратите внимание: для этого требуется написать отражение для каждого типа, хотя это тривиально с макросами. Кроме того, вы не можете использовать Reflect::Reflect в качестве ключевого типа. (Вы можете сделать это по-другому, с помощью признаков, но тогда вы, специализирующимся в пространстве имен черты, который является немного сложнее с несколькими файлами.)

#define REFLECT_TEMPLATE(TEMPL) \ 
    namespace Reflect {    \ 
     struct TEMPL {};    \ 
    };         \ 
    template<>       \ 
    struct TEMPL<Reflect::Reflect> { \ 
     using type = Reflect::KeyType; \ 
    }; 

Если бы я тебя, я бы тоже добавьте const char* name() const к отраженному типу ...

+0

Очень интересная параллель с проблемой остановки, спасибо! – Kevin

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