未命名的非类型模板参数有什么意义?

What's the point of unnamed non-type template parameters?

本文关键字:什么 参数 类型 未命名      更新时间:2023-10-16

根据参考,非类型模板参数的名称是可选的,即使在分配默认值时也是如此(参见(1(和(2((。因此,这些模板结构是有效的:

template <int> struct Foo {};
template <unsigned long = 42> struct Bar {};

我还没有看到访问非类型参数值的可能性。 我的问题是:未命名/匿名非类型模板参数有什么意义?为什么名称是可选的?

首先,我们可以将声明与定义分开。 所以声明中的名字并没有真正的帮助。和名称可以在定义中使用

template <int> struct Foo;
template <unsigned long = 42> struct Bar;
template <int N> struct Foo {/*..*/};
template <unsigned long N> struct Bar {/*..*/};

专业化是定义的特例。

然后名称可以不使用,所以我们可能会省略它:

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;

未命名/匿名非类型模板参数的意义何在?

我能想到专业化:

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);
}

这可能有点做作。但一般来说,对于某些模板,您不想命名它们,然后您不必命名很方便。

Unamed 类型和非类型参数还允许您使用模板模板参数延迟类型实例化。

以下面的函数为例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::p ack_arguments_as_t

完整的示例可在 godbolt 上找到 这里.

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>>);
}