我正在寻找的是一种表达方式:这对所有专门化都是一样的:
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而不必指定专门化…
你不可能完全得到你想要的。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; };
您可以将id_type
作为一个类(别名声明)属性,而不是将其作为模板模板参数上的独立trait:
#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; }
但是,这种方法有一个缺点。由于trait对特定类型进行了专门化,因此它将这些类型与trait的实现耦合在一起(因为您需要非常小心专门化的位置,而不是主模板的位置)。
在编写专门化时,要注意它的位置;否则,让它编译将是一种考验,从而点燃它的自焚。
只是基于Caleth的回答和由此产生的评论的扩展。你可以保护自己不受"坏"的影响。
#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>& 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 auto& 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;
}