#c #templates #template-specialization
Вопрос:
То, что я ищу, — это способ сказать: это одинаково для всех специализаций:
template <typename T>
struct Foo {
using id_type = unsigned int; // this does not depend on T!
};
Foo::id_type theId; // Doesn't matter what the specialization is, id_type is always the same.
Я хочу получить доступ к id_type без необходимости указывать специализацию…
Комментарии:
1. Зачем вам нужно объявлять использование внутри самой структуры?
2. «Это одинаково для всех специализаций» , хотя ничто не запрещает пользователю добавлять некоторую специализацию….
3. @RoQuOTriX: Для ясности. И я хотел знать, существует ли шаблонный эквивалент статического члена класса.
Ответ №1:
Вы не можете получить именно то, о чем просите. Foo
это не класс. Foo<T>
это класс, для любого T
.
У вас может быть база без шаблона, которая содержит id_type
struct FooBase {
using id_type = unsigned int;
};
template <typename T>
struct Foo : FooBase{};
FooBase::id_type theId;
Вы можете указать параметр по умолчанию для T
template <typename T = struct FooPlaceholder>
struct Foo {
using id_type = unsigned int; // this does not depend on T!
};
Foo<>::id_type theId;
Однако ничто не мешает мне написать явную специализацию Foo
, которой не хватает (или переопределяет) id_type
.
template <> struct Foo<MyType> { };
template <> struct Foo<MyOtherType> { int id_type = 42; };
Комментарии:
1. все еще можно специализироваться
Foo<S>
на том, что не наследуется отFooBase
2. @Caleth: Я придерживаюсь вашей второй идеи: тип по умолчанию-фиктивный.
3. @Jarod42: Классы могут использоваться
final
для предотвращения производного типа. Но, похоже, нет способа предотвратить явную специализацию, которая обходит исходный шаблон. Похоже на разрыв в языке…
Ответ №2:
Вместо id_type
того, чтобы быть свойством класса (объявление псевдонима), вы можете сделать его отдельной характеристикой параметра шаблона шаблона:
#include <type_traits>
// Helper: compare with std::is_same but for
// template template parameter type arguments.
template <template <typename> typename, template <typename> typename>
struct is_same_primary_template : std::false_type {};
template <template <typename> typename TT>
struct is_same_primary_template<TT, TT> : std::true_type {};
template <template <typename> typename TT, template <typename> typename UU>
constexpr bool is_same_primary_template_v{
is_same_primary_template<TT, UU>::value};
template <typename T> struct Foo {};
template <template <typename> typename, typename Enable = void> struct id_type;
template <template <typename> typename TT>
struct id_type<TT, std::enable_if_t<is_same_primary_template_v<TT, Foo>>> {
using type = int;
};
// ...
template <template <typename> typename TT>
using id_type_t = typename id_type<TT>::type;
int main() { id_type_t<Foo> theId; }
Однако у такого подхода есть один недостаток. Поскольку признак специализирован на определенных типах, он объединяет эти типы с реализацией признака (поскольку вы хотите быть очень осторожными с расположением ваших специализаций, т. е. с расположением основного шаблона).
При написании специализации будьте осторожны с ее местоположением; или заставить ее компилироваться будет таким испытанием, которое подожжет ее самосожжение.
Ответ №3:
Просто расширение, основанное на ответе Калета и комментарии, вытекающем из этого. Вы можете как бы защитить себя от «плохих» специализаций Foo, подобных этой:
#include <iostream>
#include <type_traits>
//-------------------------------------------------------------------------
// from Caleth
struct FooBase
{
using id_type = unsigned int;
};
template <typename T>
struct Foo : FooBase
{
};
FooBase::id_type theId{};
//-------------------------------------------------------------------------
// specialization bypassing FooBase
template<>
struct Foo<char>
{
};
//-------------------------------------------------------------------------
// compile time check if someone mad a "bad" specialization, pre C 20
template<typename T>
void f(const Foo<T>amp; foo)
{
static_assert(std::is_base_of_v<FooBase, Foo<T>>);
}
//-------------------------------------------------------------------------
// C 20 concept to check for FooBase
template<typename T>
concept HasFooBase = std::is_base_of_v<FooBase, T>;
// only accepts types derived from FooBase
void g(const HasFooBase autoamp; foo)
{
}
//-------------------------------------------------------------------------
int main()
{
Foo<int> foo;
Foo<char> bar;
f(foo);
g(foo);
f(bar); // won't compile, error C2607: static assertion failed
g(bar); // won't compile, error C7602: 'g': the associated constraints are not satisfied
return 0;
}