2016-11-20 73 views
2

來加總一個元組的元素我有一個具有類似tuple的接口的自定義類。因爲我希望我的代碼儘可能通用,所以我認爲將我的算法基於函數std::getstd::tuple_size,std::tuple_element是一個好主意,因此您只需專門使用這些函數即可使用我的算法。讓我們稱之爲需要這些功能專業化的概念Tuple通過使用std :: get,std :: tuple_size,std :: tuple_element

現在我試圖總結一個Tuple的組件。該函數聲明應該是這樣的:

template <class Tuple> 
int sum_components(const Tuple& t); 

我想,有很多涉及模板編程,但我無法弄清楚如何做到這一點。

另外,我只是使用全球+ operator的超載。

我正在使用C++ 1z。

回答

8

這在中非常容易。

template<class Tuple> 
decltype(auto) sum_components(Tuple const& tuple) { 
    auto sum_them = [](auto const&... e)->decltype(auto) { 
    return (e+...); 
    }; 
    return std::apply(sum_them, tuple); 
}; 

(...+e)表示相反的摺疊方向。

在以前的版本中,正確的方法是編寫自己的apply,而不是編寫定製的實現。當你的編譯器更新時,你可以刪除代碼。

,我可以這樣做:

// namespace for utility code: 
namespace utility { 
    template<std::size_t...Is> 
    auto index_over(std::index_sequence<Is...>) { 
    return [](auto&&f)->decltype(auto){ 
     return decltype(f)(f)(std::integral_constant<std::size_t,Is>{}...); 
    }; 
    } 
    template<std::size_t N> 
    auto index_upto() { 
    return index_over(std::make_index_sequence<N>{}); 
    } 
} 
// namespace for semantic-equivalent replacements of `std` code: 
namespace notstd { 
    template<class F, class Tuple> 
    decltype(auto) apply(F&& f, Tuple&& tuple) { 
    using dTuple = std::decay_t<Tuple>; 
    auto index = ::utility::index_upto< std::tuple_size<dTuple>{} >(); 
    return index([&](auto...Is)->decltype(auto){ 
     auto target=std::ref(f); 
     return target(std::get<Is>(std::forward<Tuple>(tuple))...); 
    }); 
    } 
} 

這是相當接近std::apply。 (我濫用std::ref得到INVOKE語義)。 (它並不適用於右值調用者,但這是非常特殊的情況)。

,我建議此時升級你的編譯器。在我建議現在升級你的工作。


以上所有內容都是右對齊或左對齊。在某些情況下,二叉樹摺疊可能會更好。這很棘手。

如果您的+執行表達式模板,則由於生存期問題,上述代碼將無法正常工作。在某些情況下,您可能需要添加另一個模板類型「之後,轉換爲」以使臨時表達式樹進行評估。

+0

只需要一些'decltype(auto)'噴灑。 –

+0

@ T.C。加入口味。最初忽略它們是因爲我不想考慮表達式模板和其他混亂。從臨時自動存儲對象的字段中返回的中間結果「T &&」可能會與上述情況相沖突。但現在我認爲這是表達模板的問題。 – Yakk

+0

非常感謝。這個解決方案甚至比@ krzaq更好,因爲你使用了我以前不知道的std :: apply。我接受你的解決方案。 – Max

4

使用C++ 1z,它非常簡單,用fold expressions。首先,元組轉發到_impl函數並提供它與索引序列來訪問所有元組的元素,然後總和:

template<typename T, size_t... Is> 
auto sum_components_impl(T const& t, std::index_sequence<Is...>) 
{ 
    return (std::get<Is>(t) + ...); 
} 

template <class Tuple> 
int sum_components(const Tuple& t) 
{ 
    constexpr auto size = std::tuple_size<Tuple>{}; 
    return sum_components_impl(t, std::make_index_sequence<size>{}); 
} 

demo


A C++ 14的方法是遞歸地總結一個可變參數包:

int sum() 
{ 
    return 0; 
} 

template<typename T, typename... Us> 
auto sum(T&& t, Us&&... us) 
{ 
    return std::forward<T>(t) + sum(std::forward<Us>(us)...); 
} 

template<typename T, size_t... Is> 
auto sum_components_impl(T const& t, std::index_sequence<Is...>) 
{ 
    return sum(std::get<Is>(t)...); 
} 

template <class Tuple> 
int sum_components(const Tuple& t) 
{ 
    constexpr auto size = std::tuple_size<Tuple>{}; 
    return sum_components_impl(t, std::make_index_sequence<size>{}); 
} 

demo

C++ 11方法將是定製實現index_sequence的C++ 14方法。例如從here


由於@ildjarn在評論中指出,上述例子都採用正確的褶皺,而許多程序員希望在他們的代碼左倍。 C++的1Z版本是平凡多變:

template<typename T, size_t... Is> 
auto sum_components_impl(T const& t, std::index_sequence<Is...>) 
{ 
    return (... + std::get<Is>(t)); 
} 

demo

而C++ 14不是差很多,但也有更多的變化:

template<typename T, typename... Us> 
auto sum(T&& t, Us&&... us) 
{ 
    return sum(std::forward<Us>(us)...) + std::forward<T>(t); 
} 

template<typename T, size_t... Is> 
auto sum_components_impl(T const& t, std::index_sequence<Is...>) 
{ 
    constexpr auto last_index = sizeof...(Is) - 1; 
    return sum(std::get<last_index - Is>(t)...); 
} 

demo

+1

有趣的是,您對C++ 14使用了左側摺疊,而對於C++ 17使用了右側摺疊。 ; - ] – ildjarn

+0

@ildjarn好點。我沒有有意識地這樣做,但我認爲這是因爲我本能地想要將「已知」元素保留在左側。但是,如果他的op +不是聯想性或交換性的,那麼OP有另一個問題;) – krzaq

+0

同意,_s不應該爲'sum'(儘管'+'不一定安全 - std :: string')但總的來說,我認爲第一次暴露於摺疊表達式的人應該顯示爲左側摺疊,這是大多數C++程序員期望的結果(具有FP背景的人一旦知道兩者都不會被愚弄)是可能的)。不是對你的回答的批評,只是一個觀察。 :-D – ildjarn

相關問題