為了對嚴重依賴模板的C 17框架進行單元測驗,我嘗試撰寫輔助template類,該類生成由兩個元組給出的兩組資料型別的笛卡爾積:
**Input**: std::tuple <A, B> std::tuple<C,D,E> **Expected output**: Cartesian product of the two tuples: std::tuple<std::tuple<A,C>, std::tuple<A,D>, std::tuple<A,E>, std::tuple<B,C>, std::tuple<B,D>, std::tuple<B,E>>
我知道Boost MP11 提供了這樣的功能,但我不想僅僅為了測驗目的而包含對另一個庫的依賴。到目前為止,我想出了一個非常直接的解決方案,盡管它要求類是默認可構造的(請在此處嘗試!):
template <typename T1, typename T2,
typename std::enable_if_t<is_tuple_v<T1>>* = nullptr,
typename std::enable_if_t<is_tuple_v<T2>>* = nullptr>
class CartesianProduct {
protected:
CartesianProduct() = delete;
CartesianProduct(CartesianProduct const&) = delete;
CartesianProduct(CartesianProduct&&) = delete;
CartesianProduct& operator=(CartesianProduct const&) = delete;
CartesianProduct& operator=(CartesianProduct&&) = delete;
template <typename T, typename... Ts,
typename std::enable_if_t<std::is_default_constructible_v<T>>* = nullptr,
typename std::enable_if_t<(std::is_default_constructible_v<Ts> && ...)>* = nullptr>
static constexpr auto innerHelper(T, std::tuple<Ts...>) noexcept {
return std::make_tuple(std::make_tuple(T{}, Ts{}) ...);
}
template <typename... Ts, typename T,
typename std::enable_if_t<std::is_default_constructible_v<T>>* = nullptr,
typename std::enable_if_t<(std::is_default_constructible_v<Ts> && ...)>* = nullptr>
static constexpr auto outerHelper(std::tuple<Ts...>, T) noexcept {
return std::tuple_cat(innerHelper(Ts{}, T{}) ...);
}
public:
using type = std::decay_t<decltype(outerHelper(std::declval<T1>(), std::declval<T2>()))>;
};
template <typename T1, typename T2>
using CartesianProduct_t = typename CartesianProduct<T1, T2>::type;
同樣,當嘗試以類似的方式實體化模板類串列時(在此處嘗試)我必須做出相同的假設:我不能將它應用于具有protected/private建構式(沒有friend宣告)并且不是默認的類- 可建造。
是否有可能取消默認可構造性的限制而無需求助于一個std::integer_sequence和一個額外的幫助類?據我所知,不可能std::declval<T>()直接在方法innerHelper和outerHelper(這將解決我的問題)中使用,因為它似乎不再是一個未經評估的運算式。至少GCC 會抱怨,static assertion failed: declval() must not be used!而它似乎用 Clang 編譯得很好。
先感謝您!
uj5u.com熱心網友回復:
一種解決方法是省略函式定義,直接使用decltype來推斷回傳型別:
template<typename T1, typename T2>
class CartesianProduct {
template<typename T, typename... Ts>
static auto innerHelper(T&&, std::tuple<Ts...>&&)
-> decltype(
std::make_tuple(
std::make_tuple(std::declval<T>(), std::declval<Ts>())...));
template <typename... Ts, typename T>
static auto outerHelper(std::tuple<Ts...>&&, T&&)
-> decltype(
std::tuple_cat(innerHelper(std::declval<Ts>(), std::declval<T>())...));
public:
using type = decltype(outerHelper(std::declval<T1>(), std::declval<T2>()));
};
class Example {
Example() = delete;
Example(const Example&) = delete;
};
using T1 = std::tuple<Example>;
using T2 = std::tuple<int, double>;
static_assert(
std::is_same_v<
CartesianProduct_t<T1, T2>,
std::tuple<std::tuple<Example, int>, std::tuple<Example, double>>>);
演示。
uj5u.com熱心網友回復:
也許是這樣的:
template <typename TupleA, typename TupleB>
struct CartesianProduct {
static constexpr size_t SizeA = std::tuple_size_v<TupleA>;
static constexpr size_t SizeB = std::tuple_size_v<TupleB>;
template <size_t I>
static constexpr size_t Col = I / SizeB;
template <size_t I>
static constexpr size_t Row = I % SizeB;
template <size_t ... Is>
static
std::tuple<
std::tuple<
std::tuple_element_t<Col<Is>, TupleA>,
std::tuple_element_t<Row<Is>, TupleB>
>...>
Helper(std::index_sequence<Is...>);
using type = decltype(Helper(std::make_index_sequence<SizeA*SizeB>{}));
};
演示
uj5u.com熱心網友回復:
在 C 20因為它更短。添加模板型別應該是顯而易見的。還省略了完美的轉發樣板。
template<std::size_t...Is>
auto expand(auto f, auto ts, std::index_sequence<Is...>){
using std::get;
return f(get<Is>(ts)...);
}
template<std::size_t N>
auto expand(auto f, auto ts){
return expand(f, ts, std::make_index_sequence<N>{});
}
auto expand(auto f, auto ts){
return expand<std::tuple_size_v<decltype(ts)>>(f, ts);
}
那是我們的積木。
auto cartesian(auto ts0, auto ts1){
return expand( [&](auto...t0s ){
return std::tuple_cat( [&](auto t0s){
return expand([&](auto...t1s){
return std::make_tuple(
std::make_tuple( t0s, t1s )...
);
}, ts1);
}(t0s)... );
}, ts0);
}
或類似的東西。
然后就做
template<class T0, class T1>
using cart_t = decltype( cartesian( std::declval<T0>(), std::declval<T1>() ) );
這確實要求型別是可復制/可移動的。
有兩種明顯的方法可以解決這個問題。首先是將型別提升到標簽,做產品,然后下拉。
第二種是對型別串列而不是元組進行操作。您可能正在使用元組作為型別串列,因此我們甚至不必將其恢復為元組。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/385150.html
