2012-12-15 185 views
0

我用std::tuple_cat做參數列表的子集選擇成元組,像這樣:的std :: tuple_cat替換故障

template <class...> 
struct odds; 

template <class T1> 
struct odds<T1> 
{ 
    typedef std::tuple<T1> type; 
    static type value(T1&& t1) 
    { 
     return std::make_tuple(std::forward<T1>(t1)); 
    } 
}; 

template <class T1, class T2> 
struct odds<T1, T2> 
{ 
    typedef std::tuple<T1> type; 
    static type value(T1&& t1, T2&&) 
    { 
     return std::make_tuple(std::forward<T1>(t1)); 
    } 
}; 

template <class T1, class T2, class... TTail> 
struct odds<T1, T2, TTail...> 
{ 
     typedef decltype(std::tuple_cat(T1(), typename odds<TTail...>::type())) type; // L32 
     static type value(T1&& t1, T2&&, TTail&&... rest) 
    { 
     return std::tuple_cat(std::forward<T1>(t1), odds<TTail...>::value(std::forward<TTail>(rest)...)); // L35 
    } 
}; 

,具有以下的測試案例:

// assume <tuple>, <utility> are included at top of file 
template <class... T> 
auto foo(T... x) -> typename odds<T...>::type 
{ 
     return odds<T...>::value(x...); 
     //... 
}  
int main() { 
     auto bar = foo(5, true, 6, false); // L46 
     auto baz = odds<int, bool, int, bool>::value(5, true, 6, false); // L47 
     // bar, baz should be tuple<int,int> with value { 5, 6 } 
} 

然而,模板扣除問題出現在clang-3.1和gcc-4.7.2中:

Clang輸出:

test.cc:32:19: error: no matching function for call to 'tuple_cat' 
     typedef decltype(std::tuple_cat(T1(), typename odds<TTail...>::type())) type; 
         ^~~~~~~~~~~~~~ 
test.cc:40:30: note: in instantiation of template class 'odds<int, bool, int, bool>' requested here 
auto foo(T... x) -> typename odds<T...>::type 
          ^
test.cc:40:6: note: while substituting deduced template arguments into function template 'foo' [with T = <int, bool, int, bool>] 
auto foo(T... x) -> typename odds<T...>::type 
    ^
/usr/include/c++/v1/tuple:1063:1: note: candidate template ignored: substitution failure [with _Tuple0 = int, _Tuples = <std::__1::tuple<int>>] 
tuple_cat(_Tuple0&& __t0, _Tuples&&... __tpls) 
^ 
/usr/include/c++/v1/tuple:987:1: note: candidate function not viable: requires 0 arguments, but 2 were provided 
tuple_cat() 
^ 
test.cc:46:13: error: no matching function for call to 'foo' 
     auto bar = foo(5, true, 6, false); 
        ^~~ 
test.cc:40:6: note: candidate template ignored: substitution failure [with T = <int, bool, int, bool>] 
auto foo(T... x) -> typename odds<T...>::type 
    ^
test.cc:35:10: error: no matching function for call to 'tuple_cat' 
       return std::tuple_cat(std::forward<T1>(t1), odds<TTail...>::value(std::forward<TTail>(rest)...)); 
         ^~~~~~~~~~~~~~ 
test.cc:47:41: note: in instantiation of member function 'odds<int, bool, int, bool>::value' requested here 
     auto baz = odds<int, bool, int, bool>::value(5,true,6,false); 
              ^
/usr/include/c++/v1/tuple:1063:1: note: candidate template ignored: substitution failure [with _Tuple0 = int, _Tuples = <std::__1::tuple<int>>] 
tuple_cat(_Tuple0&& __t0, _Tuples&&... __tpls) 
^ 
/usr/include/c++/v1/tuple:987:1: note: candidate function not viable: requires 0 arguments, but 2 were provided 
tuple_cat() 
^ 
3 errors generated. 

gcc的輸出:

test.cc: In instantiation of ‘struct odds<int, bool, int, bool>’: 
test.cc:40:6: required by substitution of ‘template<class ... T> typename odds<T ...>::type foo(T ...) [with T = {int, bool, int, bool}]’ 
test.cc:46:34: required from here 
test.cc:32:74: error: no matching function for call to ‘tuple_cat(int, odds<int, bool>::type)’ 
test.cc:32:74: note: candidate is: 
In file included from test.cc:1:0: 
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.2/include/g++-v4/tuple:1027:5: note: template<class ... _Tpls, class> constexpr typename std::__tuple_cat_result<_Tpls ...>::__type std::tuple_cat(_Tpls&& ...) 
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.2/include/g++-v4/tuple:1027:5: note: template argument deduction/substitution failed: 
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.2/include/g++-v4/tuple:1024:31: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’ 
test.cc: In function ‘int main()’: 
test.cc:46:34: error: no matching function for call to ‘foo(int, bool, int, bool)’ 
test.cc:46:34: note: candidate is: 
test.cc:40:6: note: template<class ... T> typename odds<T ...>::type foo(T ...) 
test.cc:40:6: note: substitution of deduced template arguments resulted in errors seen above 
test.cc:46:34: error: unable to deduce ‘auto’ from ‘<expression error>’ 
test.cc:47:13: error: ‘value’ is not a member of ‘odds<int, bool, int, bool>’ 
test.cc:47:61: error: unable to deduce ‘auto’ from ‘<expression error>’ 

gcc的是多一點有益的這裏,特別是與錯誤

test.cc:32:74: error: no matching function for call to ‘tuple_cat(int, odds<int, bool>::type)’ 

的目標是要調用的遞歸解壓縮參數的函數,收集選擇成收集元組,並返回它。爲了以平坦的方式積累它,我使用std::tuple_cat()來平滑遞歸尾元組,添加一個頭並返回元組。轉發用於在遞歸期間不丟棄引用限定符。

在代碼的後面,解壓縮結果元組以調用不同的可變參數函數,但這超出了此錯誤的範圍。很顯然,我在某處遺漏了一些微妙但至關重要的細節,但我發現在追查潛在問題方面存在很大困難。

+0

請提供,實際上可以被編譯到重現你的錯誤一個完整的例子。 –

回答

2

tuple_cat的參數必須是你似乎與非元組類型來調用它的元組(或「元組樣」東西支持std::tuple_sizestd::tuple_element API,如std::pairstd::array),但是從錯誤信息。要做到這一點你可能想是這樣的:

std::tuple_cat(std::make_tuple(a_non_tuple), a_tuple, another_tuple); 

這會把第一個參數爲tuple<decltype(a_non_tuple)>因此它可以與其他元組連接起來。

來連接到std::tuple<T1, std::tuple<T2, std::tuple<T3, ...>>>std::tuple<T1, T2, T3, ...>

這不是 「串聯」。我認爲正確的說法是「扁平化」。

我不太確定你在嘗試什麼,因爲你還沒有提供完整的例子。你打電話foo有一個像std::tuple<T1, std::tuple<T2, std::tuple<T3, ...>>>的論點?

這是行不通的,因爲foo將演繹Tstd::tuple<T1, std::tuple<T2, std::tuple<T3, ...>>>和實例化odds<T1>特殊化,這只是包裝內另一元組的參數foo

+0

賓果。我錯過了http://en.cppreference.com/w/cpp/utility/tuple/tuple_cat中可變參數示例中的準元組。用'typename std :: tuple ()'替換'decltype'中的'T1()',並在'std :: make_tuple()'內部封裝轉發的't1'修復了它。 – moshbear

+0

強調「必須是元組」和最後一段中的「非」用於接受和upvote。 – moshbear

+2

Sheesh,我沒有看到所有的代碼並且解決了問題,我猜想什麼是錯誤的,對於upvote來說還不夠嗎?!你很難取悅;)我已經重新安排了將相關段落放在首位的答案。 –

1

我不知道這對你是否有意思,因爲它是一種替代實現,而不是對你的問題的回答。我認爲使用tuple_cat就像在運行時會做很多移動/拷貝一樣,儘管編譯器可能會避免這些不重要的類型。無論如何,這似乎是一個有趣的TMP問題。

這就是我想出來的(這很可能過於冗長);它肯定可以使用一些清理工具(以及幾個星期前的cla語版本 - 在嘗試編譯它時聲明)。

有趣的遞歸位在Skip,它使用前一個元組作爲模式(count,really)來構造下一個;您可以看到最終賠率是如何通過使用SkipHelper傳遞兩個參數的模式來設置初始呼叫。 (這部分肯定可以使用一些工作,也許它會一直使用更方便的數字):

namespace skip_args { 
namespace detail { 

// We use Tuple as a kind of generic typelist. 
template<typename ...T> 
using Tuple = std::tuple<T...>; 

// Utility "functions" 
// Pusher<T, Tuple<U...> >::type = Tuple<T, U...> 
template<typename A, typename B> struct Pusher; 
template<typename T, typename ...U> struct Pusher<T, Tuple<U...>> { 
    using type = Tuple<T, U...>; 
}; 
template<typename A, typename B> 
using push = typename Pusher<A, B>::type; 

// Skip is an intermediate used to skip over ignored elements. 
// To allow recursion, we declare the general form first. 
// All three arguments are Tuples. 
template<typename Next, typename Rest, typename This> struct Skip; 

// Node is actually used to store some data item. It also inherits from the next 
// following Node (if there is one) so that we end up with an inheritance chain. 
// (That's the part similar to libstdc++ tuples; it makes the layout the same, 
// too, as long as there's no EBO to deal with, because we don't bother here.) 
template<typename Rest, typename ...This> struct Node; 

template<typename ...R_, typename T, typename ...T_> 
struct Node<Tuple<R_...>, T, T_...> 
: Skip<Tuple<>, Tuple<R_...>, Tuple<T, T_...>>::type { 
    using self = Node<Tuple<R_...>, T, T_...>; 
    using next = typename Skip<Tuple<>, Tuple<R_...>, Tuple<T, T_...>>::type; 
    // Recursive construction of node types 
    using nodes = push<self, typename next::nodes>; 
    using value_type = T; 
    // The constructor takes all the arguments, uses the first one, skips some, 
    // and passes the rest to the next node. 
    constexpr Node<Tuple<R_...>, T, T_...>(T&& t, T_&&...t_, R_&&...r_) 
     : next(std::forward<R_>(r_)...), value(std::forward<T>(t)) { 
    } 
    T&& value; 
}; 

// Base: 
template<> 
struct Node<Tuple<>> { 
    using nodes = Tuple<>; 
    constexpr Node<Tuple<>>() {} 
}; 

// Skip (N...) (R R...) (T T...) => Skip (N... R) (R...) (T...) 
// In other words, it drops elements from the third tuple, and for each one it 
// moves an element from the second tuple to the first tuple. If it runs out of 
// the third tuple, it "returns" a new Node. If it runs out of the second tuple, 
// then we're done, but to satisfy the node requirements, it actually needs to 
// declare a constructor (which drops all its arguments) 

// General case: 
template<typename ...N_, typename R, typename ...R_, typename T, typename ...T_> 
struct Skip<Tuple<N_...>, Tuple<R, R_...>, Tuple<T, T_...>> 
    : Skip<Tuple<N_..., R>, Tuple<R_...>, Tuple<T_...>> { 
}; 
// Ran out of pattern 
template<typename ...N_, typename R_, typename T_> 
struct Skip<Tuple<N_...>, R_, T_> { 
    using type = Node<R_, N_...>; 
    using nodes = typename type::nodes; 
}; 

template<typename T> struct TupleTyper; 
template<typename ...T> struct TupleTyper<Tuple<T...>> { 
    using type = Tuple<typename T::value_type...>; 
}; 

template<typename A, typename B, typename C> struct TupleMaker; 
template<typename Tup, typename H, typename ...T> 
struct TupleMaker<Tup, H, Tuple<T...>> { 
    Tup operator()(H&& helper) { 
    return Tup(static_cast<T&>(helper).value...); 
    } 
}; 

template<typename N> struct SkipHelper { 
    using tuple_type = typename TupleTyper<typename N::nodes>::type; 
    template<typename ...U> 
    tuple_type operator()(U&& ...u) { 
    return TupleMaker<tuple_type, N, typename N::nodes>()(N(std::forward<U>(u)...)); 
    } 
}; 

} // namespace detail 

template<typename ...T> struct Odds; 

template<typename T1, typename T2, typename ...T_> 
struct Odds<T1, T2, T_...> 
    : detail::SkipHelper<detail::Node<detail::Tuple<T_...>, T1, T2>> { 
}; 
template<typename T1> 
struct Odds<T1> : detail::SkipHelper<detail::Node<detail::Tuple<>, T1>> { 
}; 

// tuple_from_odds takes any number of arguments, 
// and returns a tuple of the odd numbered ones. 
template<typename...T> auto tuple_from_odds(T&&...t) 
    -> typename Odds<T...>::tuple_type { 
    return Odds<T...>()(std::forward<T>(t)...); 
} 

} // namespace skip_args 
+0

好泛化! – moshbear

+0

@moshbear:我有點想出爲什麼鏗鏘墜毀;這是因爲我使用std :: tuple作爲一種類型列表。有一些關於std :: tuple的libstdC++實現,它與clang不一致。無論如何,我改變了'使用Tuple = std :: tuple;'到一個簡單的類型列表結構('template struct Tuple {};'然後只在需要的地方使用'std :: tuple'一個'std :: tuple',這使得clang再次開心,我記得當我需要TMP類型的列表時,爲什麼總是使用我自己的TypeList結構。 – rici