此代碼編譯結合如何完美轉發用括號括初始化
tuple<pair<int,int>,unique_ptr<int>> t({0,0}, unique_ptr<int>());
原因是第三個叫tuple(const Types&...)
,但它似乎是一個任意的限制。
C++ 11無法使用可變參數模板來表示它,還是有可能?
此代碼編譯結合如何完美轉發用括號括初始化
tuple<pair<int,int>,unique_ptr<int>> t({0,0}, unique_ptr<int>());
原因是第三個叫tuple(const Types&...)
,但它似乎是一個任意的限制。
C++ 11無法使用可變參數模板來表示它,還是有可能?
這是可能的,但並不平凡。爲了完成這個工作,包含N
參數的元組必須支持2^N
構造函數,對於每個T
,所有組合的T&&
和T const&
。
我們要做的就是在這些可以使用繼承完成的構造函數中混合使用這些2^N
。由於基類的構造函數只能用using
顯式提供,所以我們只能添加固定數量基類的構造函數,所以我們必須使用遞歸。
一種方法是從0
到2^N
,並且如果第i位是1或者是右值,則使ith參數爲const-ref。總共需要2^N
基類,每個基類添加一個構造函數到他們的直接基地。
namespace detail {
// A bitlist holds N powers of two: 1, 2, 4, 8, 16, ...
template <std::size_t... i> struct bitlist { using type = bitlist; };
template <std::size_t N, typename=bitlist<>>
struct make_bitlist;
template <std::size_t N, std::size_t... i>
struct make_bitlist<N, bitlist<i...>>
: make_bitlist<N-1, bitlist<0,1+i...>> {};
template <std::size_t... i> struct make_bitlist<0, bitlist<i...>>
: bitlist<(1<<i)...> {};
struct forward_tag {}; // internal struct that nobody else should use
// if T is a reference, some constructors may be defined twice, so use a non-accessible type.
template <bool B, typename T>
using const_if_set = typename std::conditional<B,
typename std::conditional<std::is_reference<T>::value, forward_tag, T const&>::type, T&&>::type;
// Our helper class. Each tuple_constructor is derived from N-1 others
// each providing one constructor. N shall equal (1<<sizeof...(T))-1
template <std::size_t N, typename L, typename... T> struct tuple_constructor;
template <std::size_t N, std::size_t... I, typename... T>
struct tuple_constructor<N, bitlist<I...>, T...>
: tuple_constructor<N-1, bitlist<I...>, T...>
{ // inherit base constructors
using tuple_constructor<N-1, bitlist<I...>, T...>::tuple_constructor;
tuple_constructor(const_if_set<(N & I), T>... t)
: tuple_constructor<N-1, bitlist<I...>, T...>
(forward_tag{}, std::forward<const_if_set<(N & I), T>>(t)...) {}
};
// base case: N=0, we finally derive from std::tuple<T...>
template <std::size_t... I, typename... T>
struct tuple_constructor<0, bitlist<I...>, T...> : std::tuple<T...> {
tuple_constructor(T&&... t)
: tuple_constructor(forward_tag{}, std::forward<T&&>(t)...) {}
// All constructor calls are forwarded to this one
template <typename... T2>
tuple_constructor(forward_tag, T2&&... t2)
: std::tuple<T...>(std::forward<T2>(t2)...) {}
};
// Convenience using for N=2^n, bitlist=1,2,4,...,2^n where n = sizeof...(T)
template <typename... T>
using better_tuple_base = tuple_constructor
< (1<<sizeof...(T)) - 1, typename make_bitlist<sizeof...(T)>::type, T... >;
}
template <typename... T> struct better_tuple : detail::better_tuple_base<T...> {
using typename detail::better_tuple_base<T...>::tuple_constructor;
};
但要注意,這並不能很好地擴展到大元組和顯著增加編譯時間。在我看來,這是語言的一個限制。
還有另一種方法。類型擦除構造一個'T'。這個構造函數需要一個'template ctor(U &&)'和一個(非模板)'ctor(T &&)',並存儲如何從其參數構造一個'T'。 '{}'調用'T &&'構造函數,其他的東西調用'U &&'構造函數。必須在非類型化內存(!)中構造各個字段,這個類需要一個類型擦除的概念類型爲「void(void *)」的構造方法,從而構造其概念性的「T」。不止有點瘋狂,但避免了指數式的爆炸。可能難以優化運行時間成本。 – Yakk
'std :: tuple'的構造函數''T''會做這項工作。這樣的構造函數會導致一個構建+一個非引用類型的移動(意味着對於那些沒有移動構造函數的類型需要一個額外的副本構造),一個構建比我們擁有的構造更多。 –