未命名的非类型模板参数有什么意义?
What's the point of unnamed non-type template parameters?
根据the reference,非类型模板参数的名称是可选的,即使在分配默认值时也是如此(参见(1)和(2))。因此这些模板结构是有效的:
template <int> struct Foo {};
template <unsigned long = 42> struct Bar {};
我还没有看到访问非类型参数值的可能性。
我的问题是:unnamed/anonymous 非类型模板参数有什么意义?为什么名称是可选的?
What's the point of unnamed/anonymous non-type template parameters?
我能想到的专业:
template<int = 42>
struct Foo{
char x;
};
template<>
struct Foo<0> {
int x;
};
template<>
struct Foo<1> {
long x;
};
然后:
Foo<0> a; // x data member is int
Foo<1> b; // x data member is long
Foo<7> c; // x data member is char
Foo<> d; // x data member is char
哦,你可以访问它们!
template <int> struct Foo {};
template <int N>
int get(Foo<N>) {
return N;
}
int main() {
Foo<3> foo;
return get(foo);
}
这可能有点做作。但一般来说,对于某些模板,您不想命名它们,这样您就不必命名很方便。
首先,我们可以将声明与定义分开。
所以声明中的名称并不是很有帮助。和名称可能会在定义中使用
template <int> struct Foo;
template <unsigned long = 42> struct Bar;
template <int N> struct Foo {/*..*/};
template <unsigned long N> struct Bar {/*..*/};
专业化是定义的特例。
那么name可以不用,所以可以省略:
template <std::size_t, typename T>
using always_t = T;
template <std::size_t ... Is, typename T>
struct MyArray<std::index_sequence<Is...>, T>
{
MyArray(always_t<Is, const T&>... v) : /*..*/
};
或用于 SFINAE
template <typename T, std::size_t = T::size()>
struct some_sized_type;
未命名类型和非类型参数还允许您使用模板-模板参数延迟类型实例化。
以下面函数中的destination_type
为例
它可以解析为具有模板类型参数和 0 到 N 模板值参数的任何类型。
template <template <typename, auto...> typename destination_type, typename TupleType>
constexpr auto repack(TupleType && tuple_value)
{
return [&tuple_value]<std::size_t ... indexes>(std::index_sequence<indexes...>) {
return destination_type{std::get<indexes>(tuple_value)...};
}(std::make_index_sequence<std::tuple_size_v<TupleType>>{});
}
static_assert(repack<std::array>(std::tuple{1,2,3}) == std::array{1,2,3});
当您需要对参数包进行抽象时,这种机制会派上用场。
这里,例如,我们不关心 Ts...
是包含多个参数的参数包,还是扩展为本身具有多个模板参数的单一类型。
-> 可以从模板类型参数转换为模板值参数。
见gcl::mp::type_traits::pack_arguments_as_t
上提供了完整示例
template <template <typename ...> class T, typename ... Ts>
class pack_arguments_as {
template <template <typename...> class PackType, typename... PackArgs>
constexpr static auto impl(PackType<PackArgs...>)
{
return T<PackArgs...>{};
}
template <typename... PackArgs>
constexpr static auto impl(PackArgs...)
{
return T<PackArgs...>{};
}
public:
using type = decltype(impl(std::declval<Ts>()...));
};
template <template <typename ...> class T, typename ... Ts>
using pack_arguments_as_t = typename pack_arguments_as<T, Ts...>::type;
namespace tests
{
template <typename... Ts>
struct pack_type {};
using toto = pack_arguments_as_t<std::tuple, pack_type<int, double, float>>;
using titi = pack_arguments_as_t<std::tuple, int, double, float>;
static_assert(std::is_same_v<toto, titi>);
static_assert(std::is_same_v<toto, std::tuple<int, double, float>>);
static_assert(std::is_same_v<pack_type<int, double, float>, pack_arguments_as_t<pack_type, toto>>);
}
根据the reference,非类型模板参数的名称是可选的,即使在分配默认值时也是如此(参见(1)和(2))。因此这些模板结构是有效的:
template <int> struct Foo {};
template <unsigned long = 42> struct Bar {};
我还没有看到访问非类型参数值的可能性。 我的问题是:unnamed/anonymous 非类型模板参数有什么意义?为什么名称是可选的?
What's the point of unnamed/anonymous non-type template parameters?
我能想到的专业:
template<int = 42>
struct Foo{
char x;
};
template<>
struct Foo<0> {
int x;
};
template<>
struct Foo<1> {
long x;
};
然后:
Foo<0> a; // x data member is int
Foo<1> b; // x data member is long
Foo<7> c; // x data member is char
Foo<> d; // x data member is char
哦,你可以访问它们!
template <int> struct Foo {};
template <int N>
int get(Foo<N>) {
return N;
}
int main() {
Foo<3> foo;
return get(foo);
}
这可能有点做作。但一般来说,对于某些模板,您不想命名它们,这样您就不必命名很方便。
首先,我们可以将声明与定义分开。 所以声明中的名称并不是很有帮助。和名称可能会在定义中使用
template <int> struct Foo;
template <unsigned long = 42> struct Bar;
template <int N> struct Foo {/*..*/};
template <unsigned long N> struct Bar {/*..*/};
专业化是定义的特例。
那么name可以不用,所以可以省略:
template <std::size_t, typename T>
using always_t = T;
template <std::size_t ... Is, typename T>
struct MyArray<std::index_sequence<Is...>, T>
{
MyArray(always_t<Is, const T&>... v) : /*..*/
};
或用于 SFINAE
template <typename T, std::size_t = T::size()>
struct some_sized_type;
未命名类型和非类型参数还允许您使用模板-模板参数延迟类型实例化。
以下面函数中的destination_type
为例
它可以解析为具有模板类型参数和 0 到 N 模板值参数的任何类型。
template <template <typename, auto...> typename destination_type, typename TupleType>
constexpr auto repack(TupleType && tuple_value)
{
return [&tuple_value]<std::size_t ... indexes>(std::index_sequence<indexes...>) {
return destination_type{std::get<indexes>(tuple_value)...};
}(std::make_index_sequence<std::tuple_size_v<TupleType>>{});
}
static_assert(repack<std::array>(std::tuple{1,2,3}) == std::array{1,2,3});
当您需要对参数包进行抽象时,这种机制会派上用场。
这里,例如,我们不关心 Ts...
是包含多个参数的参数包,还是扩展为本身具有多个模板参数的单一类型。
-> 可以从模板类型参数转换为模板值参数。
见gcl::mp::type_traits::pack_arguments_as_t
上提供了完整示例template <template <typename ...> class T, typename ... Ts>
class pack_arguments_as {
template <template <typename...> class PackType, typename... PackArgs>
constexpr static auto impl(PackType<PackArgs...>)
{
return T<PackArgs...>{};
}
template <typename... PackArgs>
constexpr static auto impl(PackArgs...)
{
return T<PackArgs...>{};
}
public:
using type = decltype(impl(std::declval<Ts>()...));
};
template <template <typename ...> class T, typename ... Ts>
using pack_arguments_as_t = typename pack_arguments_as<T, Ts...>::type;
namespace tests
{
template <typename... Ts>
struct pack_type {};
using toto = pack_arguments_as_t<std::tuple, pack_type<int, double, float>>;
using titi = pack_arguments_as_t<std::tuple, int, double, float>;
static_assert(std::is_same_v<toto, titi>);
static_assert(std::is_same_v<toto, std::tuple<int, double, float>>);
static_assert(std::is_same_v<pack_type<int, double, float>, pack_arguments_as_t<pack_type, toto>>);
}