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

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

godbolt here.

上提供了完整示例
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>>);
}