Поскольку вопрос точно описал проблему, с которой я столкнулся в своем коде (я использую Visual Studio 2015), я нашел альтернативное решение, которым хотел бы поделиться.
Идея заключается в следующем: вместо передачи параметра шаблона шаблона в шаблон класса ExampleTemplate
можно также передать обычное имя типа, содержащее тип DummyType
в качестве фиктивного параметра, скажем std::vector<DummyType>
.
Затем внутри класса один фиктивный параметр заменяется чем-то разумным.Для замены типа могут использоваться следующие вспомогательные классы:
// this is simply the replacement for a normal type:
// it takes a type T, and possibly replaces it with ReplaceByType
template<typename T, typename ReplaceWhatType, typename ReplaceByType>
struct replace_type
{
using type = std::conditional_t<std::is_same<T, ReplaceWhatType>::value, ReplaceByType, T>;
};
// this sets up the recursion, such that replacement also happens
// in contained nested types
// example: in "std::vector<T, allocator<T> >", both T's are replaced
template<template<typename ...> class C, typename ... Args, typename ReplaceWhatType, typename ReplaceByType>
struct replace_type<C<Args ...>, ReplaceWhatType, ReplaceByType>
{
using type = C<typename replace_type<Args, ReplaceWhatType, ReplaceByType>::type ...>;
};
// an alias for convenience
template<typename ... Args>
using replace_type_t = typename replace_type<Args ...>::type;
Обратите внимание на рекурсивный шаг в replace_type
, который учитывает, что типы, вложенные в другие классы, также заменяются - например, этимв std::vector<T, allocator<T> >
заменены оба T
, а не только первый.То же самое касается нескольких иерархий вложений.
Далее вы можете использовать это в своем ExampleTemplate
-классе,
struct DummyType {};
template <typename C, typename T>
struct ExampleTemplate
{
replace_type_t<C, DummyType, T> items;
};
и вызывать его через
int main()
{
ExampleTemplate<std::vector<DummyType>, float> a;
a.items.push_back(1.0);
//a.items.push_back("Hello"); // prints an error message which shows that DummyType is replaced correctly
ExampleTemplate<std::list<DummyType>, float> b;
b.items.push_back(1.0);
//b.items.push_back("Hello"); // prints an error message which shows that DummyType is replaced correctly
ExampleTemplate<std::map<int, DummyType>, float> c;
c.items[0]=1.0;
//c.items[0]="Hello"; // prints an error message which shows that DummyType is replaced correctly
}
DEMO
Помимо не очень приятного синтаксиса, это имеет то преимущество, что
Работает с любым количеством параметров шаблона по умолчанию- например, рассмотрим случай с std::map
в примере.
Нет необходимости явно указывать какие-либо параметры шаблона по умолчанию.
Его можно легко расширить на более фиктивные параметры (тогда как пользователи, вероятно, не должны вызывать его ...).
Кстати: вместо фиктивного типа выможно также использовать std::placeholder
... только что понял, что это может быть немного лучше.