菊花链可变参数模板 类
Daisy chain variadic templated classes
我有一个 class 模板管道:
template <typename A, typename B> class Pipeline;
我想创建一个接受任意数量的任意 Pipeline
s 的可变参数函数模板,并且我想以一种棘手的方式限制它们。非可变代码看起来像:
Pipeline<A, C> compose(Pipeline<A, B> p1, Pipeline<B, C> p2);
Pipeline<A, D> compose(Pipeline<A, B> p1, Pipeline<B, C> p2, Pipeline<C, D> p3);
// ...and so on
现在是否有可能以可变的方式端到端地约束它们?
// I would like to write something like:
Pipeline<Args[0], Args[len(Args)-1]> compose(Pipeline<Args[i], Args[i+1]> ps...);
我假设您的 Pipeline
看起来像:
template <typename A, typename B>
struct Pipeline {
using first = A;
using second = B;
};
首先,让我们为什么是有效链创建一个类型特征 link:
template <typename P1, typename P2>
struct is_valid_link : std::false_type { };
template <typename A, typename B, typename C>
struct is_valid_link<Pipeline<A,B>, Pipeline<B,C>> : std::true_type { };
接下来,我们借用@Columbo的bool_pack
技巧来验证一堆bool
都是true
:
template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
当然我们需要索引序列技巧:
template <typename... Pipelines,
typename R = decltype(detail::daisy_chain(
std::make_index_sequence<sizeof...(Pipelines)-1>(),
std::declval<Pipelines>()...))
>
R compose(Pipelines... pipelines)
{
return {};
}
大部分工作在此处进行检查:
namespace detail {
template <size_t... Is,
typename... Pipelines,
typename T = std::tuple<Pipelines...>,
typename R = std::enable_if_t<
// ensure that all our pairwise pipelines are valid links
all_true<
is_valid_link<std::tuple_element_t<Is,T>,
std::tuple_element_t<Is+1,T>>::value...
>::value,
// pick out the first and last types
Pipeline<typename std::tuple_element_t<0, T>::first,
typename std::tuple_element_t<sizeof...(Pipelines)-1, T>::second>
>>
R daisy_chain(std::index_sequence<Is...>, Pipelines... pipelines);
}
这让我们可以做到:
int main() {
Pipeline<int, double> p = compose(Pipeline<int, char>{}, Pipeline<char, double>{});
}
这样写的好处是您仍然拥有 SFINAE - 如果您需要的话。这样:
auto invalid = compose(Pipeline<int, char>{}, Pipeline<float, double>{});
会触发重载解析失败:
main.cpp:46:31: error: no matching function for call to 'compose'
Pipeline<int, double> p = compose(Pipeline<int, char>{}, Pipeline<float, double>{});
^~~~~~~
main.cpp:40:3: note: candidate template ignored: substitution failure [with Pipelines = <Pipeline<int, char>, Pipeline<float, double>>]: no matching function for call to 'daisy_chain'
R compose(Pipelines... pipelines)
^
1 error generated.
我有一个 class 模板管道:
template <typename A, typename B> class Pipeline;
我想创建一个接受任意数量的任意 Pipeline
s 的可变参数函数模板,并且我想以一种棘手的方式限制它们。非可变代码看起来像:
Pipeline<A, C> compose(Pipeline<A, B> p1, Pipeline<B, C> p2);
Pipeline<A, D> compose(Pipeline<A, B> p1, Pipeline<B, C> p2, Pipeline<C, D> p3);
// ...and so on
现在是否有可能以可变的方式端到端地约束它们?
// I would like to write something like:
Pipeline<Args[0], Args[len(Args)-1]> compose(Pipeline<Args[i], Args[i+1]> ps...);
我假设您的 Pipeline
看起来像:
template <typename A, typename B>
struct Pipeline {
using first = A;
using second = B;
};
首先,让我们为什么是有效链创建一个类型特征 link:
template <typename P1, typename P2>
struct is_valid_link : std::false_type { };
template <typename A, typename B, typename C>
struct is_valid_link<Pipeline<A,B>, Pipeline<B,C>> : std::true_type { };
接下来,我们借用@Columbo的bool_pack
技巧来验证一堆bool
都是true
:
template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
当然我们需要索引序列技巧:
template <typename... Pipelines,
typename R = decltype(detail::daisy_chain(
std::make_index_sequence<sizeof...(Pipelines)-1>(),
std::declval<Pipelines>()...))
>
R compose(Pipelines... pipelines)
{
return {};
}
大部分工作在此处进行检查:
namespace detail {
template <size_t... Is,
typename... Pipelines,
typename T = std::tuple<Pipelines...>,
typename R = std::enable_if_t<
// ensure that all our pairwise pipelines are valid links
all_true<
is_valid_link<std::tuple_element_t<Is,T>,
std::tuple_element_t<Is+1,T>>::value...
>::value,
// pick out the first and last types
Pipeline<typename std::tuple_element_t<0, T>::first,
typename std::tuple_element_t<sizeof...(Pipelines)-1, T>::second>
>>
R daisy_chain(std::index_sequence<Is...>, Pipelines... pipelines);
}
这让我们可以做到:
int main() {
Pipeline<int, double> p = compose(Pipeline<int, char>{}, Pipeline<char, double>{});
}
这样写的好处是您仍然拥有 SFINAE - 如果您需要的话。这样:
auto invalid = compose(Pipeline<int, char>{}, Pipeline<float, double>{});
会触发重载解析失败:
main.cpp:46:31: error: no matching function for call to 'compose'
Pipeline<int, double> p = compose(Pipeline<int, char>{}, Pipeline<float, double>{});
^~~~~~~
main.cpp:40:3: note: candidate template ignored: substitution failure [with Pipelines = <Pipeline<int, char>, Pipeline<float, double>>]: no matching function for call to 'daisy_chain'
R compose(Pipelines... pipelines)
^
1 error generated.