跨参数包分发模板包装器
Distribute template wrapper across parameter pack
我有几个模板类型,Egg<T>
和 Chick<T>
。
template<typename T>
struct Egg{};
template<typename T>
struct Chick{};
小鸡在 class LoudNest<Chicks...>
中,鸡蛋在 QuietNest<Eggs...>
:
template <typename... Chicks>
struct LoudNest {
std::tuple<Chicks...> chicks;
};
template <typename... Eggs>
struct QuietNest {
std::tuple<Eggs...> eggs;
// more here.
};
我想在 QuietNest<Eggs...>
上使用一个 hatch
方法来生成 LoudNest。对于 QuietNest 中的每个 Egg<T>
,LoudNest 应该有一个 Chick<T>
。我有一个函数 QuietNest<Eggs...>::hatch_impl
,它可以创建一个 std::tuple<Chicks...>
,其中 Chick
都具有正确的类型参数。也就是说,QuietNest<Egg<double>, Egg<string>, Egg<char>>::hatch_impl
将 return std::tuple<Chick<double>, Chick<string>, Chick<char>>
。我在尝试将其包装在 LoudNest
构造函数中时遇到困难:
template <typename... Eggs>
struct QuietNest {
std::tuple<Eggs...> eggs;
auto hatch() const {
// hatchlings is a std::tuple of chicks templated how I want.
auto hatchlings = std::apply(
[&](auto... args) { return hatch_impl(std::make_tuple(), args...); },
eggs);
// This line causes an error:
return LoudNest{hatchlings};
// error: cannot refer to class template 'LoudNest' without a template argument
}
// The rest of this all works, but is included in case you want to poke at it:
// base case: only one parameter was passed—the tuple of hatched chicks.
template<typename...Chicks>
std::tuple<Chicks...> hatch_impl(std::tuple<Chicks...> chicks) {
return chicks;
}
// recursive case: in addition to the tuple of hatched chicks,
// at least one egg was passed (possibly more in the tail)
template<typename...Chicks, typename T, typename...Unhatched>
std::tuple<Chicks..., Chick<T>> hatch_impl(
std::tuple<Chicks...> chicks,
const Egg<T>& egg,
Unhatched... tail
) const {
Chick<T> babyBird = hatchOne(egg);
return hatch_impl(
std::tuple_cat(chicks, std::make_tuple(babyBird)),
tail...);
}
template<T>
Chick<T> hatchOne(Egg<T> egg) { return Chick<T>{}; }
};
我在想我需要制作一个 "converter" 来接受鸡蛋的参数包并生成带有相应类型小鸡的 LoudNest。从将单个 Egg<T>
转换为 Chick<T>
开始,我有:
template<typename T>
struct AsChick {
using type = T;
};
template< template <typename> class E, typename T>
struct AsChick<E<T>> {
static_assert(std::is_same<E<T>, Egg<T>>::value, "Expected AsChick to be used with an Egg<T>");
using type = Chick<T>;
};
我遇到困难的地方是当我尝试对参数包执行相同操作时:
template<typename... Eggs>
struct AsLoudNest1 {
using type = LoudNest<
(AsChick<Eggs>::type)...
// I want this to expand Eggs to produce
// AsChick<Eggs0>::type, AsChick<Eggs1>::type, AsChick<Eggs2>::type, ...
// but it doesn't looks like that's a supported type of expansion
>;
};
static_assert(std::is_same<
AsLoudNest1<Egg<int>, Egg<double>>::type,
LoudNest<Chick<int>, Chick<double>>
>::value, "Expected AsLoudNest1 to convert my Egg<T>s to Chick<T>s");
然后尝试第二个:
template <
class E, // does this need to be template<typename> class E?
typename... Rest>
struct AsLoudNest2 {
using type = LoudNest<
// Pretty sure the beginning is right.
AsChick<E>::type,
// This line feels wrong, AsLoudNest2<...>::type is a concrete type, not a parameter pack
AsLoudNest2<Rest...>::type...
>;
};
// also, feels like I need a base case for AsLoudNest2?
我的实际问题与实现解释器有关,classes 是 FormalParameter<T>
(Egg<T>
)、ActualParameter<T>
(Chick<T>
)等等。但是,我想避免在示例代码中使用 "parameter" 这个词,因为我们已经在讨论不同意义上的参数包。
来自此 post 的代码:https://godbolt.org/z/XBIEhm
我可以通过一些更改来修复您的示例:https://godbolt.org/z/3VW68f
向LoudNest
添加推导指南以缓解LoudNest{hatchlings}
中推导Chicks...
类型的问题(这可能不是唯一的解决方案,但似乎干净,因为它不需要使 hatch() 实现复杂化:
template<typename... Chicks>
LoudNest(const std::tuple<Chicks...>& chicks) -> LoudNest<Chicks...>;
(添加 hatchOne
,它出现在你的问题中,但不是你分享的神栓 link)
摆脱 hatch_impl
支持在包扩展期间调用 hatchOne
:
auto hatchlings = std::apply(
[&](auto... args) { return std::make_tuple(hatchOne(args)...); },
eggs);
使用专业化将 Egg 参数的内部 T
类型推导为 AsLoudNest1
:
template<typename... Eggs>
struct AsLoudNest1 {};
template<typename... Ts>
struct AsLoudNest1<Egg<Ts>...> {
using type = LoudNest<Chick<Ts>...>;
};
我有几个模板类型,Egg<T>
和 Chick<T>
。
template<typename T>
struct Egg{};
template<typename T>
struct Chick{};
小鸡在 class LoudNest<Chicks...>
中,鸡蛋在 QuietNest<Eggs...>
:
template <typename... Chicks>
struct LoudNest {
std::tuple<Chicks...> chicks;
};
template <typename... Eggs>
struct QuietNest {
std::tuple<Eggs...> eggs;
// more here.
};
我想在 QuietNest<Eggs...>
上使用一个 hatch
方法来生成 LoudNest。对于 QuietNest 中的每个 Egg<T>
,LoudNest 应该有一个 Chick<T>
。我有一个函数 QuietNest<Eggs...>::hatch_impl
,它可以创建一个 std::tuple<Chicks...>
,其中 Chick
都具有正确的类型参数。也就是说,QuietNest<Egg<double>, Egg<string>, Egg<char>>::hatch_impl
将 return std::tuple<Chick<double>, Chick<string>, Chick<char>>
。我在尝试将其包装在 LoudNest
构造函数中时遇到困难:
template <typename... Eggs>
struct QuietNest {
std::tuple<Eggs...> eggs;
auto hatch() const {
// hatchlings is a std::tuple of chicks templated how I want.
auto hatchlings = std::apply(
[&](auto... args) { return hatch_impl(std::make_tuple(), args...); },
eggs);
// This line causes an error:
return LoudNest{hatchlings};
// error: cannot refer to class template 'LoudNest' without a template argument
}
// The rest of this all works, but is included in case you want to poke at it:
// base case: only one parameter was passed—the tuple of hatched chicks.
template<typename...Chicks>
std::tuple<Chicks...> hatch_impl(std::tuple<Chicks...> chicks) {
return chicks;
}
// recursive case: in addition to the tuple of hatched chicks,
// at least one egg was passed (possibly more in the tail)
template<typename...Chicks, typename T, typename...Unhatched>
std::tuple<Chicks..., Chick<T>> hatch_impl(
std::tuple<Chicks...> chicks,
const Egg<T>& egg,
Unhatched... tail
) const {
Chick<T> babyBird = hatchOne(egg);
return hatch_impl(
std::tuple_cat(chicks, std::make_tuple(babyBird)),
tail...);
}
template<T>
Chick<T> hatchOne(Egg<T> egg) { return Chick<T>{}; }
};
我在想我需要制作一个 "converter" 来接受鸡蛋的参数包并生成带有相应类型小鸡的 LoudNest。从将单个 Egg<T>
转换为 Chick<T>
开始,我有:
template<typename T>
struct AsChick {
using type = T;
};
template< template <typename> class E, typename T>
struct AsChick<E<T>> {
static_assert(std::is_same<E<T>, Egg<T>>::value, "Expected AsChick to be used with an Egg<T>");
using type = Chick<T>;
};
我遇到困难的地方是当我尝试对参数包执行相同操作时:
template<typename... Eggs>
struct AsLoudNest1 {
using type = LoudNest<
(AsChick<Eggs>::type)...
// I want this to expand Eggs to produce
// AsChick<Eggs0>::type, AsChick<Eggs1>::type, AsChick<Eggs2>::type, ...
// but it doesn't looks like that's a supported type of expansion
>;
};
static_assert(std::is_same<
AsLoudNest1<Egg<int>, Egg<double>>::type,
LoudNest<Chick<int>, Chick<double>>
>::value, "Expected AsLoudNest1 to convert my Egg<T>s to Chick<T>s");
然后尝试第二个:
template <
class E, // does this need to be template<typename> class E?
typename... Rest>
struct AsLoudNest2 {
using type = LoudNest<
// Pretty sure the beginning is right.
AsChick<E>::type,
// This line feels wrong, AsLoudNest2<...>::type is a concrete type, not a parameter pack
AsLoudNest2<Rest...>::type...
>;
};
// also, feels like I need a base case for AsLoudNest2?
我的实际问题与实现解释器有关,classes 是 FormalParameter<T>
(Egg<T>
)、ActualParameter<T>
(Chick<T>
)等等。但是,我想避免在示例代码中使用 "parameter" 这个词,因为我们已经在讨论不同意义上的参数包。
来自此 post 的代码:https://godbolt.org/z/XBIEhm
我可以通过一些更改来修复您的示例:https://godbolt.org/z/3VW68f
向
LoudNest
添加推导指南以缓解LoudNest{hatchlings}
中推导Chicks...
类型的问题(这可能不是唯一的解决方案,但似乎干净,因为它不需要使 hatch() 实现复杂化:template<typename... Chicks> LoudNest(const std::tuple<Chicks...>& chicks) -> LoudNest<Chicks...>;
(添加
hatchOne
,它出现在你的问题中,但不是你分享的神栓 link)摆脱
hatch_impl
支持在包扩展期间调用hatchOne
:auto hatchlings = std::apply( [&](auto... args) { return std::make_tuple(hatchOne(args)...); }, eggs);
使用专业化将 Egg 参数的内部
T
类型推导为AsLoudNest1
:template<typename... Eggs> struct AsLoudNest1 {}; template<typename... Ts> struct AsLoudNest1<Egg<Ts>...> { using type = LoudNest<Chick<Ts>...>; };