C ++专业化中的多个模板参数包
C++ Multiple template parameter packs in specialization
我正在尝试使用参数包并得到了一些我无法解释的行为(在我的理解中它应该是有效的)。
我有一个采用一种类型和使用两个参数包 (http://coliru.stacked-crooked.com/a/0cb6c6fef7b09e6f) 的特化的模板化结构:
#include <type_traits>
template<class, int> struct dummy{};
template<class...> struct multiple{};
template<class T, class = std::true_type>
struct exactly_one_not_0 : std::false_type {};
template<class...LTs, class...RTs, class T, int V>
struct exactly_one_not_0
<
multiple
<
dummy<LTs, 0>...,
dummy<T, V>,
dummy<RTs, 0>...
>,
std::bool_constant<V>
> : std::true_type
{};
template<class T>
constexpr bool exactly_one_not_0_v = exactly_one_not_0<T>::value;
using d0 = dummy<int,0>;
using d1 = dummy<int,1>;
static_assert(exactly_one_not_0_v<multiple<d1>>);
static_assert(exactly_one_not_0_v<multiple<d1,d0>>);
static_assert(exactly_one_not_0_v<multiple<d0,d1>>);
static_assert(exactly_one_not_0_v<multiple<d0,d1,d0>>);
static_assert(!exactly_one_not_0_v<multiple<>>);
static_assert(!exactly_one_not_0_v<multiple<d0>>);
static_assert(!exactly_one_not_0_v<multiple<d0,d0>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d0,d1,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d0,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d1,d0>>);
前四个断言失败。为什么?专业化应该能够捕捉到这些情况。
相比之下,以下代码按预期工作 (http://coliru.stacked-crooked.com/a/871ee1cc28f5ddc9):
#include <iostream>
template<class T>
struct multi_pack
{
static void f() {std::cout << "base case\n";}
};
template<class...Ts1, class...Ts2>
struct multi_pack<std::pair<std::tuple<Ts1...>, std::tuple<Ts2...>>>
{
using T = std::pair<std::tuple<Ts1...>, std::tuple<Ts2...>>;
static void f() {std::cout << "special case\n";}
};
知道为什么第一个示例失败而第二个示例有效吗?
在第二种情况下,您要求编译器匹配不同类型中的包(有一对 Ts1
的元组和 Ts2
的元组)。绝对不含糊。
在你的第一个例子中,两个包都用在同一个参数包中,由第三种类型分开。我明白你为什么期望它能工作,但显然编译器(gcc 和 clang)拒绝完全匹配前缀包。
在您的特定情况下,它实际上在几种情况下是不明确的:由于 V 是一个参数,它也可能是 0,因此匹配 d0
的序列是不明确的。无论如何,我试过即使使用常量 1 也不能解决问题。您将不得不自己去除前缀。
#include <type_traits>
template<class, int> struct dummy{};
template<class...> struct multiple{};
template<class T, class = void> // <-- was this supposed to be an enabler?
struct exactly_one_not_0 : std::false_type {};
template<class Ts, class...RTs, int V>
struct exactly_one_not_0
<
multiple
<
dummy<Ts, V>,
dummy<RTs, 0>...
>,
std::enable_if_t<V!=0>
> : std::bool_constant<V>
{};
template<class LT, class...RDummies>
struct exactly_one_not_0
<
multiple
<
dummy<LT, 0>,
RDummies...
>
> : exactly_one_not_0<multiple<RDummies...>>
{};
template<class T>
constexpr bool exactly_one_not_0_v = exactly_one_not_0<T>::value;
using d0 = dummy<int,0>;
using d1 = dummy<int,1>;
static_assert(exactly_one_not_0_v<multiple<d1>>);
static_assert(exactly_one_not_0_v<multiple<d1,d0>>);
static_assert(exactly_one_not_0_v<multiple<d0,d1>>);
static_assert(exactly_one_not_0_v<multiple<d0,d1,d0>>);
static_assert(!exactly_one_not_0_v<multiple<>>);
static_assert(!exactly_one_not_0_v<multiple<d0>>);
static_assert(!exactly_one_not_0_v<multiple<d0,d0>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d0,d1,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d0,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d1,d0>>);
int main()
{
return 0;
}
为了不实例化多个 multiple
,您可能还想将其删除
template<class T>
struct exactly_one_not_0 : std::false_type {};
template<class Enable, class... Ts> // <-- was this supposed to be an enabler?
struct exactly_one_not_0_impl : std::false_type {};
template<class... Ts>
struct exactly_one_not_0<multiple<Ts...>>
: exactly_one_not_0_impl<void, Ts...>
{};
template<class Ts, class...RTs, int V>
struct exactly_one_not_0_impl
<
std::enable_if_t<V!=0>,
dummy<Ts, V>,
dummy<RTs, 0>...
> : std::bool_constant<V>
{};
template<class LT, class...RDummies>
struct exactly_one_not_0_impl
<
void,
dummy<LT, 0>,
RDummies...
> : exactly_one_not_0_impl<void, RDummies...>
{};
我正在尝试使用参数包并得到了一些我无法解释的行为(在我的理解中它应该是有效的)。
我有一个采用一种类型和使用两个参数包 (http://coliru.stacked-crooked.com/a/0cb6c6fef7b09e6f) 的特化的模板化结构:
#include <type_traits>
template<class, int> struct dummy{};
template<class...> struct multiple{};
template<class T, class = std::true_type>
struct exactly_one_not_0 : std::false_type {};
template<class...LTs, class...RTs, class T, int V>
struct exactly_one_not_0
<
multiple
<
dummy<LTs, 0>...,
dummy<T, V>,
dummy<RTs, 0>...
>,
std::bool_constant<V>
> : std::true_type
{};
template<class T>
constexpr bool exactly_one_not_0_v = exactly_one_not_0<T>::value;
using d0 = dummy<int,0>;
using d1 = dummy<int,1>;
static_assert(exactly_one_not_0_v<multiple<d1>>);
static_assert(exactly_one_not_0_v<multiple<d1,d0>>);
static_assert(exactly_one_not_0_v<multiple<d0,d1>>);
static_assert(exactly_one_not_0_v<multiple<d0,d1,d0>>);
static_assert(!exactly_one_not_0_v<multiple<>>);
static_assert(!exactly_one_not_0_v<multiple<d0>>);
static_assert(!exactly_one_not_0_v<multiple<d0,d0>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d0,d1,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d0,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d1,d0>>);
前四个断言失败。为什么?专业化应该能够捕捉到这些情况。
相比之下,以下代码按预期工作 (http://coliru.stacked-crooked.com/a/871ee1cc28f5ddc9):
#include <iostream>
template<class T>
struct multi_pack
{
static void f() {std::cout << "base case\n";}
};
template<class...Ts1, class...Ts2>
struct multi_pack<std::pair<std::tuple<Ts1...>, std::tuple<Ts2...>>>
{
using T = std::pair<std::tuple<Ts1...>, std::tuple<Ts2...>>;
static void f() {std::cout << "special case\n";}
};
知道为什么第一个示例失败而第二个示例有效吗?
在第二种情况下,您要求编译器匹配不同类型中的包(有一对 Ts1
的元组和 Ts2
的元组)。绝对不含糊。
在你的第一个例子中,两个包都用在同一个参数包中,由第三种类型分开。我明白你为什么期望它能工作,但显然编译器(gcc 和 clang)拒绝完全匹配前缀包。
在您的特定情况下,它实际上在几种情况下是不明确的:由于 V 是一个参数,它也可能是 0,因此匹配 d0
的序列是不明确的。无论如何,我试过即使使用常量 1 也不能解决问题。您将不得不自己去除前缀。
#include <type_traits>
template<class, int> struct dummy{};
template<class...> struct multiple{};
template<class T, class = void> // <-- was this supposed to be an enabler?
struct exactly_one_not_0 : std::false_type {};
template<class Ts, class...RTs, int V>
struct exactly_one_not_0
<
multiple
<
dummy<Ts, V>,
dummy<RTs, 0>...
>,
std::enable_if_t<V!=0>
> : std::bool_constant<V>
{};
template<class LT, class...RDummies>
struct exactly_one_not_0
<
multiple
<
dummy<LT, 0>,
RDummies...
>
> : exactly_one_not_0<multiple<RDummies...>>
{};
template<class T>
constexpr bool exactly_one_not_0_v = exactly_one_not_0<T>::value;
using d0 = dummy<int,0>;
using d1 = dummy<int,1>;
static_assert(exactly_one_not_0_v<multiple<d1>>);
static_assert(exactly_one_not_0_v<multiple<d1,d0>>);
static_assert(exactly_one_not_0_v<multiple<d0,d1>>);
static_assert(exactly_one_not_0_v<multiple<d0,d1,d0>>);
static_assert(!exactly_one_not_0_v<multiple<>>);
static_assert(!exactly_one_not_0_v<multiple<d0>>);
static_assert(!exactly_one_not_0_v<multiple<d0,d0>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d0,d1,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d0,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d1,d0>>);
int main()
{
return 0;
}
为了不实例化多个 multiple
,您可能还想将其删除
template<class T>
struct exactly_one_not_0 : std::false_type {};
template<class Enable, class... Ts> // <-- was this supposed to be an enabler?
struct exactly_one_not_0_impl : std::false_type {};
template<class... Ts>
struct exactly_one_not_0<multiple<Ts...>>
: exactly_one_not_0_impl<void, Ts...>
{};
template<class Ts, class...RTs, int V>
struct exactly_one_not_0_impl
<
std::enable_if_t<V!=0>,
dummy<Ts, V>,
dummy<RTs, 0>...
> : std::bool_constant<V>
{};
template<class LT, class...RDummies>
struct exactly_one_not_0_impl
<
void,
dummy<LT, 0>,
RDummies...
> : exactly_one_not_0_impl<void, RDummies...>
{};