2015-02-10 26 views
2

這是相當不錯的(不是我),比如如何ü可以擴展(或「爆炸」)的元組作爲參數傳遞給函數:「解壓」的std ::陣列<T,N>作爲參數傳遞給函數

template<int ...I> struct index_tuple_type { 
    template<int N> using append = index_tuple_type<I..., N>; 
}; 

template<int N> struct make_index_impl { 
    using type = typename make_index_impl<N-1>::type::template append<N-1>; 
}; 

template<> struct make_index_impl<0> { using type = index_tuple_type<>; }; 

template<int N> using index_tuple = typename make_index_impl<N>::type; 

template <typename I, typename ...Args> 
struct func_traits; 

template <typename R, int ...I, typename ...Args> 
struct func_traits<R, index_tuple_type<I...>, Args...> { 
    template <typename TT, typename FT> 
    static inline R call(TT &&t, FT &&f) { 
    return f(std::get<I>(std::forward<TT>(t))...); 
    } 
}; 

template< 
    typename FT, 
    typename ...Args, 
    typename R = typename std::result_of<FT(Args&&...)>::type 
> 
inline R explode(std::tuple<Args...>& t, FT &&f) { 
    return func_traits<R, index_tuple<sizeof...(Args)>, Args...> 
    ::call(t, std::forward<FT>(f)); 
} 

那麼你可以用這個像這樣:

void test1(int i, char c) { 
    printf("%d %c\n", i, c); 
} 

int main() { 
    std::tuple<int, char> t1{57, 'a'}; 
    explode(t1, test1); 
} 

Live version

我是隨便你怎麼可以做std::array同樣的事情,因爲它很喜歡元組。 std::get<N>std::array一起使用,所以我認爲這將很容易修改此解決方案。但是像這樣的東西是行不通的:

template< 
    typename FT, 
    typename Arg, 
    std::size_t I, 
    typename R = typename std::result_of<FT(Arg&&)>::type 
> 
inline R explode(std::array<Arg, I>& t, FT &&f) { 
    return func_traits<R, index_tuple<I>, Arg>:: 
    call(t, std::forward<FT>(f)); 
} 

void test2(int i1, int i2) { 
    printf("%d %d\n", i1, i2); 
} 

int main() { 
    std::array<int, int> t1{1, 2}; 
    explode(t2, test1); 
} 

因爲部分std::result_of<FT(Arg&&)>::type。參數類型Arg&&是錯誤的,result_of沒有字段type。對於元組Args&&...展開,但現在應該是「重複」I次。有沒有辦法做到這一點使用result_of所以返回的類型可以扣除?

此外,我想知道,有工具來「解壓」 tuplearray,纔有可能以「解壓」遞歸(可能使用enable_if)像tuple<array<int, 2>, tuple<array<double,3>, ...等結構?某種樹tuplearray是分支,其他類型是樹葉?

+1

帶有作爲類型模板參數編碼的左值引用的'std :: forward'等於「no-op」。你可能打算把't'作爲它的論點。和順便說一句,使用[尾隨返回類型像這裏](http://coliru.stacked-crooked.com/a/5af1aac65ba014d9)而不是 – 2015-02-10 19:35:45

+0

你有沒有考慮過,而不是考慮做一個函數,將數組轉換爲引用的元組? – Hurkyl 2015-02-10 19:42:04

+0

@PiotrS。糾正了'前鋒',我的錯誤擴展宏是錯誤的。 @Hurkyl是的,我想過了,但是這不是一回事。 – 2015-02-10 19:49:13

回答

8
// enable argument dependent lookup on `get` call: 
namespace aux { 
    using std::get; 
    template<size_t N, class T> 
    auto adl_get(T&&)->decltype(get<N>(std::declval<T>())); 
} 
using aux::adl_get; 
template<class F, class TupleLike, size_t...Is> 
auto explode(F&& f, TupleLike&& tup, std::index_sequence<Is...>) 
-> std::result_of_t< F(decltype(adl_get<Is>(std::forward<TupleLike>(tup)))...) > 
{ 
    using std::get; // ADL support 
    return std::forward<F>(f)(get<Is>(std::forward<TupleLike>(tup))...); 
} 

是第一步。 std::index_sequence是C++ 14,但它很容易在C++ 11中實現。

接下來的步驟也很簡單。

首先,決定什麼類型的元組,像一個traits類。我會繼續前進,只鴨式使用它們,但一些功能和特性的類,我們將使用不SFINAE友好:

template<class T> 
struct tuple_like:std::false_type{}; 
template<class... Ts> 
struct tuple_like<std::tuple<Ts...>>:std::true_type{}; 
template<class... Ts> 
struct tuple_like<std::pair<Ts...>>:std::true_type{}; 
template<class T, size_t N> 
struct tuple_like<std::array<T,N>>:std::true_type{}; 

接下來,explode過載,僅適用於tuple_like類型:

template<class F, class TupleLike, 
    class TupleType=std::decay_t<TupleLike>, // helper type 
    class=std::enable_if_t<tuple_like<TupleType>{}>> // SFINAE tuple_like test 
auto explode(F&& f, TupleLike&& tup) 
-> decltype(
    explode(
    std::declval<F>(), 
    std::declval<TupleLike>(), 
    std::make_index_sequence<std::tuple_size<TupleType>{}>{} 
) 
) 
{ 
    using indexes = std::make_index_sequence<std::tuple_size<TupleType>{}>; 
    return explode(
    std::forward<F>(f), 
    std::forward<TupleLike>(tup), 
    indexes{} 
    ); 
} 

如果缺乏constexpr支持,你需要更改一些{}::value

上面提到了成對,數組或元組的技巧。如果要添加其他的元組樣類型的支持,簡直就是一個專業化添加到tuple_like並確保std::tuple_size正確專門爲你的類型和get<N>是ADL超載(在該類型的封閉命名空間)。


std::make_index_sequence也是C++ 14,但易於在C++ 11中編寫。

template<size_t...> 
struct index_sequence{}; 
namespace details { 
    template<size_t count, size_t...Is> 
    struct mis_helper:mis_helper<count-1, count-1, Is...> {}; 
    template<size_t...Is> 
    struct mis_helper<0,Is...> { 
    using type=index_sequence<Is...>; 
    }; 
} 
template<size_t count> 
using make_index_sequence=typename details::mis_helper<count>::type; 

(這是差Qoi相對於C++ 14庫,因爲它需要O(n)的模板遞歸模板實例爲大小爲n的列表,它至少應該使用登錄下降,然而,爲n少於幾百,這沒關係)。

std::enable_if_t<?>是C++ 14,但在C++ 11中只是typename std::enable_if<?>::type

+1

爲什麼要重載而不僅僅是採用'TupleLike'並使用'tuple_size'? – 2015-02-10 19:45:44

+1

@ T.C。當tuple_size被遞交給非tuple/array/pair時,未指定的行爲意味着它不會執行乾淨的SFINAE。根據我的經驗,這種東西的用例可以從SFINAE中獲益。 – Yakk 2015-02-10 19:56:52

+0

@ T.C。那裏。 SFINAE檢查'tuple_like',一個轉發'TupleType'。 – Yakk 2015-02-10 20:08:15

相關問題