2013-07-18 189 views
3

我正在玩元組和模板。我知道如果練習你會使用boost :: fusion(我認爲)來做這種事情。我試圖通過一個元組來實現相當於std :: accumulate。積累在值的元組

下面是我的代碼。就在我可以告訴編譯錯誤是由它嘗試使用4模板參數版本引起的,當我打算使用3模板參數版本來完成遞歸時。這意味着我錯過了某些功能重載解決方案。

我曾經想過,因爲兩個函數都可以匹配,所以它會選擇3個模板參數版本作爲更好的匹配,因爲最後一個參數類型是明確聲明的。如果我將std :: tuple_size作爲附加模板參數添加到tuple_accumulate_helper的兩個版本,我仍然會得到相同的行爲。

任何人都可以建議我做錯了什麼?

#include <tuple> 

template <std::size_t I> 
struct int_{}; 

template <typename T, typename OutT, typename OpT, std::size_t IndexI> 
auto tuple_accumulate_helper(T tuple, OutT init, OpT op, int_<IndexI>) -> decltype(tuple_accumulate_helper(tuple, op(init, std::get<IndexI>(tuple)), op, int_<IndexI + 1>())) 
{ 
    return tuple_accumulate_helper(tuple, op(init, std::get<IndexI>(tuple)), op, int_<IndexI + 1>()); 
} 

template <typename T, typename OutT, typename OpT> 
auto tuple_accumulate_helper(T tuple, OutT init, OpT op, int_<std::tuple_size<T>::value>) -> decltype(init) 
{ 
    return init; 
} 

template <typename T, typename OutT, typename OpT> 
auto tuple_accumulate(T tuple, OutT init, OpT op) -> decltype(tuple_accumulate_helper(tuple, init, op, int_<0>())) 
{ 
    return tuple_accumulate_helper(tuple, init, op, int_<0>()); 
} 

struct functor 
{ 
    template <typename T1, typename T2> 
    auto operator()(T1 t1, T2 t2) -> decltype(t1 + t2) 
    { 
     return t1 + t2; 
    } 
}; 

int main(int argc, const char* argv[]) 
{ 
    auto val = tuple_accumulate(std::make_tuple(5, 3.2, 7, 6.4f), 0, functor()); 
    return 0; 
} 
+1

VS是落後的合規性。使用在線編譯器(例如http://coliru.stacked-crooked.com/或http://ideone.com)獲取來自多個最新編譯器安裝的意見。根據Clang的說法,你有一個索引溢出錯誤,使用索引= 4到一個大小爲4的元組中。VC說什麼? – Potatoswatter

+0

你的代碼看起來很好,除了int_ :: value>應該是int_ :: value-1>。它沒有在gcc4.8.1上編譯。它適用於msvc2013 v120。 –

+0

通過clang運行它看起來,當我期待它選擇帶有3個模板參數的重載時,它選擇帶有4個模板參數的重載,然後無法實例化元組末尾的std :: get。 – Graznarak

回答

2

你不能讓編譯器來匹配模板專業化依賴於嵌套類型,std::tuple_size<T>::value在你的榜樣,[Partial specialization with type nested in a templated class],所以你必須得找個替代的方式來讓編譯器知道遞歸結束時。

在下面的代碼片段中,我提供了一個替代方案。我絕不要求我的解決辦法是由任何措施「最好的」,但我認爲它可能在向你展示如何處理這個問題是有幫助的:

#include <cstddef> 
#include <tuple> 
#include <utility> 
#include <type_traits> 
#include <iostream> 


//deduces the type resulted from the folding of a sequence from left to right 
//avoids the decltype nonsense 
template <typename T, typename OpT> 
class result_of_acumulate; 

template <typename... ArgsT, typename OpT> 
class result_of_acumulate<std::tuple<ArgsT...>, OpT> 
{ 
private: 
    template <typename... ArgsHeadT> 
    struct result_of_acumulate_helper; 

    template <typename ArgsHeadT> 
    struct result_of_acumulate_helper<ArgsHeadT> 
    { 
     typedef ArgsHeadT type; 
    }; 

    template <typename ArgsHead1T, typename ArgsHead2T> 
    struct result_of_acumulate_helper<ArgsHead1T, ArgsHead2T> 
    { 
     typedef typename std::result_of<OpT(ArgsHead1T, ArgsHead2T)>::type type; 
    }; 

    template <typename ArgsHead1T, typename ArgsHead2T, typename... ArgsTailT> 
    struct result_of_acumulate_helper<ArgsHead1T, ArgsHead2T, ArgsTailT...> 
    { 
     typedef typename result_of_acumulate_helper<typename std::result_of<OpT(ArgsHead1T, ArgsHead2T)>::type, ArgsTailT...>::type type; 
    }; 

public: 
    typedef typename result_of_acumulate_helper<ArgsT...>::type type; 
}; 

template <std::size_t IndexI, typename T, typename OutT, typename OpT> 
constexpr typename std::enable_if<(IndexI == std::tuple_size<T>::value), OutT>::type 
tuple_accumulate_helper(T const& /*tuple*/, OutT const& init, OpT /*op*/) 
{ 
    return init; 
} 

template <std::size_t IndexI, typename T, typename OutT, typename OpT> 
constexpr typename std::enable_if 
< 
    (IndexI < std::tuple_size<T>::value), 
    typename result_of_acumulate<T, OpT>::type 
>::type 
tuple_accumulate_helper(T const& tuple, OutT const init, OpT op) 
{ 
    return tuple_accumulate_helper<IndexI + 1>(tuple, op(init, std::get<IndexI>(tuple)), op); 
} 

template <typename T, typename OutT, typename OpT> 
auto tuple_accumulate(T const& tuple, OutT const& init, OpT op) 
    -> decltype(tuple_accumulate_helper<0>(tuple, init, op)) 
{ 
    return tuple_accumulate_helper<0>(tuple, init, op); 
} 

struct functor 
{ 
    template <typename T1, typename T2> 
    auto operator()(T1 t1, T2 t2) 
     -> decltype(t1 + t2) 
    { 
     return t1 + t2; 
    } 
}; 

int main(int /*argc*/, const char* /*argv*/[]) 
{ 
    auto val = tuple_accumulate(std::make_tuple(5, 3.2, 7U, 6.4f), 0L, functor()); 
    std::cout << val << std::endl; //should output 21.6 
    return 0; 
} 

使用gcc(GCC)4.8成功編譯和測試。 1 20130725(預發佈)在Archlinux x64盒子上。

+0

+1因爲它的工作原理,而在我的升壓示例所需時間的1/10時進行編譯:/ – sehe

0

一個建議:因爲std :: tuples可以攜帶非數值類型,所以你應該讓你的函數知道這種可能性,並在編譯時警告你。

下面是一個使用SFINAE

建議
struct functor 
{ 
    template <typename T1, typename T2, 
    typename std::enable_if< std::is_arithmetic<T1>::value == true && 
    std::is_arithmetic<T2>::value == true , bool>::type = false > 
    auto operator()(T1 t1, T2 t2) -> decltype(t1 + t2) 
    { 
     return t1 + t2; 
    } 
}; 

型性狀來限制你想在你的模板函數/類/仿函數接受類型非常重要

更新1:使用static_assert另一個建議(這會在觸發時給出明確的錯誤信息)

struct functor 
{ 
    template <typename T1, typename T2 > 
    auto operator()(T1 t1, T2 t2) -> decltype(t1 + t2) 
    { 
     static_assert(std::is_arithmetic<T1>::value == true && 
    std::is_arithmetic<T2>::value == true) 
     return t1 + t2; 
    } 
}; 
4

我不知道你是否有興趣,但如果你可以使用一點點提升,你可以有這樣的 「開箱即用」:

#include <boost/fusion/adapted/std_tuple.hpp> 
#include <boost/fusion/include/algorithm.hpp> 
#include <boost/phoenix/phoenix.hpp> 

using namespace boost::phoenix::arg_names; 

#include <iostream> 

int main() 
{ 
    auto t = std::make_tuple(5, 3.2, 7, 6.4f); 

    std::cout << boost::fusion::accumulate(t, 0, arg1 + arg2) << std::endl; 
    std::cout << boost::fusion::accumulate(t, 1, arg1 * arg2) << std::endl; 
} 

打印

21.6 
716.8 
+0

在[Coliru]上(http://coliru.stacked-crooked.com/a/8fcd253f6849d1be) – sehe