[英]api for indexed variadic arguments
我不知道在將類似元組的對象解包到可調用處理程序中時,即在使用std::apply
時,是否有一種好的和干凈的方法來索引可變參數 arguments 。
這是一個不完美但相當干凈的解決方案:
const auto animals = std::make_tuple("cow", "dog", "sheep");
// handwritten, stateful, bad...
std::apply([](const auto& ... str){
const auto print = [](const auto& str, size_t index){
std::cout << index << ": " << str << '\n';
};
// this should not be done by the user!!!
size_t i = 0;
(print(str, i++), ...);
}, animals);
此解決方案比使用std::index_sequence
的重載更干凈,因為您不必在 lambda 的 scope 之外編寫任何代碼。塊 scope 內不允許使用模板,因此需要在外部創建一些幫助程序 class。
這很糟糕,因為有一個由用戶創建的可變 state。 應該沒有這樣的東西,索引應該是隱式和按需可用的。
這是我認為需要的以及到目前為止我設法實現的:
const auto animals = std::make_tuple("cow", "dog", "sheep");
// desired
// JavaScript-style destructuring.
// C++ structured bindings are not allowed as arguments
// apply([](auto ... {value, index}){ ... }, animals);
// still bad, but better - index is implicit and constant
std::apply(indexed([](auto ... indexedValue){
const auto print = [](const auto& indexedValue){
const auto &[index, value] = indexedValue;
std::cout << index << ": " << value << '\n';
};
(print(indexedValue), ...);
}), animals);
C++ 不允許像 function arguments 這樣的結構化綁定,這是非常不幸的。 無論如何,我認為這個 api 比手動遞增計數器或編寫一些樣板幫助程序更好。
您所要做的就是
跟隨該死的火車
將您的可調用對象包裝到indexed()
function 中。
而且它不需要對 STL 的部分進行任何修改。
但是,我的實施遠非最佳。 它產生的指令比第一個例子多得多:https://godbolt.org/z/3G4doao39
這是我對indexed()
function 的實現,我想對其進行更正。
#include <cstddef>
#include <type_traits>
#include <tuple>
namespace detail {
template <size_t I, typename T>
struct _indexed
{
constexpr static size_t index = I;
T value;
constexpr _indexed(std::integral_constant<size_t, I>, T t)
: value(t)
{}
template <size_t Elem>
friend constexpr auto get(const detail::_indexed<I, T>& v) noexcept ->
std::tuple_element_t<Elem, detail::_indexed<I, T>>{
if constexpr (Elem == 0)
return I;
if constexpr (Elem == 1)
return v.value;
}
};
template <size_t I, typename T>
_indexed(std::integral_constant<size_t, I>, T) -> _indexed<I, T>;
template <typename CRTP>
class _add_indices
{
public:
template <typename ... Args>
constexpr decltype(auto) operator()(Args &&... args) const noexcept {
return (*this)(std::make_index_sequence<sizeof...(Args)>(), std::forward<Args>(args)...);
}
private:
template <typename ... Args, size_t ... I>
constexpr decltype(auto) operator()(std::index_sequence<I...>, Args ... args) const noexcept {
// does not compile
// return std::invoke(&CRTP::callable, static_cast<CRTP const&>(*this),
// _indexed(std::integral_constant<size_t, I>{}, std::forward<Args>(args))...);
return static_cast<const CRTP&>(*this).callable(_indexed(std::integral_constant<size_t, I>{}, std::forward<Args>(args))...);
}
};
}
template <size_t I, typename T>
struct std::tuple_size<detail::_indexed<I, T>> : std::integral_constant<size_t, 2> {};
template <size_t I, typename T>
struct std::tuple_element<0, detail::_indexed<I, T>>
{
using type = size_t;
};
template <size_t I, typename T>
struct std::tuple_element<1, detail::_indexed<I, T>>
{
using type = T;
};
template <typename Callable>
constexpr auto indexed(Callable c) noexcept{
struct _c : detail::_add_indices<_c> {
Callable callable;
};
return _c{.callable = c};
}
// api:
// apply(indexed([](auto ... indexedValue){}), tuple);
如果我正確理解你想要什么......在我看來你只需要一個類/結構如下
template <typename Callable>
struct indexed_call
{
Callable c;
template <std::size_t ... Is, typename ... As>
constexpr auto call (std::index_sequence<Is...>, As && ... as) const {
return c(std::pair{Is, std::forward<As>(as)}...);
}
template <typename ... As>
constexpr auto operator() (As && ... as) const {
return call(std::index_sequence_for<As...>{}, std::forward<As>(as)...);
}
};
和一個明確的、瑣碎的、演繹指南
template <typename C>
indexed_call(C) -> indexed_call<C>;
並且調用稍微改變如下
std::apply(indexed_call{[](auto ... indexedValue){
const auto print = [](const auto& iV){
const auto &[index, value] = iV;
std::cout << index << ": " << value << '\n';
};
(print(indexedValue), ...);
}}, animals);
或者,如果您願意,也可以簡化通話
std::apply(indexed_call{[](auto ... iV){
((std::cout << iV.first << ": " << iV.second << '\n'), ...);
}}, animals);
下面是一個完整的編譯示例
#include <type_traits>
#include <iostream>
#include <tuple>
template <typename Callable>
struct indexed_call
{
Callable c;
template <std::size_t ... Is, typename ... As>
constexpr auto call (std::index_sequence<Is...>, As && ... as) const {
return c(std::pair{Is, std::forward<As>(as)}...);
}
template <typename ... As>
constexpr auto operator() (As && ... as) const {
return call(std::index_sequence_for<As...>{}, std::forward<As>(as)...);
}
};
template <typename C>
indexed_call(C) -> indexed_call<C>;
int main(){
const auto animals = std::make_tuple("cow", "dog", "sheep");
std::apply(indexed_call{[](auto ... indexedValue){
const auto print = [](const auto& iV){
const auto &[index, value] = iV;
std::cout << index << ": " << value << '\n';
};
(print(indexedValue), ...);
}}, animals);
std::apply(indexed_call{[](auto ... iV){
((std::cout << iV.first << ": " << iV.second << '\n'), ...);
}}, animals);
}
- - - 編輯 - - -
OP寫道
我不認為利用 std::pair 在語義上是正確的。 最好有.value, .index
我通常更喜歡使用標准組件,當功能等效時,但如果你想要帶有value
和index
組件的東西,你可以添加一個簡單的結構(根據你的喜好命名)
template <typename V>
struct val_with_index
{
std::size_t index;
V value;
};
和另一個瑣碎的顯式演繹指南
template <typename V>
val_with_index(std::size_t, V) -> val_with_index<V>;
那么你必須修改call()
方法來使用它而不是std::pair
template <std::size_t ... Is, typename ... As>
constexpr auto call (std::index_sequence<Is...>, As && ... as) const {
return c(val_with_index{Is, std::forward<As>(as)}...);
} // ......^^^^^^^^^^^^^^
現在適用於雙 lambda 案例
對於簡化的情況,顯然你必須用index
和value
改變first
和second
std::apply(indexed_call{[](auto ... iV){
((std::cout << iV.index << ": " << iV.value << '\n'), ...);
}}, animals); // ....^^^^^...............^^^^^
又是完整的編譯示例
#include <type_traits>
#include <iostream>
#include <tuple>
template <typename V>
struct val_with_index
{
std::size_t index;
V value;
};
template <typename V>
val_with_index(std::size_t, V) -> val_with_index<V>;
template <typename Callable>
struct indexed_call
{
Callable c;
template <std::size_t ... Is, typename ... As>
constexpr auto call (std::index_sequence<Is...>, As && ... as) const {
return c(val_with_index{Is, std::forward<As>(as)}...);
}
template <typename ... As>
constexpr auto operator() (As && ... as) const {
return call(std::index_sequence_for<As...>{}, std::forward<As>(as)...);
}
};
template <typename C>
indexed_call(C) -> indexed_call<C>;
int main(){
const auto animals = std::make_tuple("cow", "dog", "sheep");
std::apply(indexed_call{[](auto ... indexedValue){
const auto print = [](const auto& iV){
const auto &[index, value] = iV;
std::cout << index << ": " << value << '\n';
};
(print(indexedValue), ...);
}}, animals);
std::apply(indexed_call{[](auto ... iV){
((std::cout << iV.index << ": " << iV.value << '\n'), ...);
}}, animals);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.