2015-01-06 37 views
3

在C++中我有它的一些元素的元組:在運行時從元組中提供模板函數元素?

std::tuple <int, char> my_tuple(3, 'q'); 

還有一些模板功能,完美的作品無論在整數和字符:

template <class T> void my_function(T); 

現在,假設在運行時我想在我元組的其中一個元素上運行my_function(但我不知道是哪個)。我注意到,這是不可能做這樣的事情:

unsigned int n; 

// Give a value to n 

my_function(std::get <n> (my_tuple)); 

然而,在原則上我所需要的應該是相同的東西,如:

unsigned int n; 

// Give a value to n 

switch(n) 
{ 
    case 0: 
     my_function(std::get <0> (my_tuple)); 
     break; 
    case 1: 
     my_function(std::get <1> (my_tuple)); 
     break; 
    default: 
     // Do nothing or throw an exception 
} 

因此它的聲音對我來說,這應該是可行的.. 是嗎?

+0

的std :: get返回類型不管它是什麼,它應該得到執行的模板函數的n個元組元素,所以它看起來應該是可能的。你試過了嗎?它不起作用嗎? – Colin

+0

我試着做一些像'unsigned int n; CIN >> N; my_function(std :: get (my_tuple))''但是我得到了'沒有匹配的函數來調用 –

回答

3

n是一個運行時值,它不能用於在編譯時實例化模板。您的switch的工作原理是因爲您手動將每個std::get<N>實例化,並將它們連接到相應的運行時值。

但是,編寫braindead switch樹是件麻煩事。爲什麼不讓編譯器生成帶有一點TMP的樣板文件?

#include <tuple> 
#include <cassert> 
#include <iostream> 

template <class T> 
void my_function(T); 

// Test specialisations to see what's going on 
template <> void my_function(int i) { std::cout << "int " << i << '\n'; } 
template <> void my_function(char c) { std::cout << "char " << c << '\n'; } 

namespace detail { 
    // Available in std in C++14 
    template <bool P, class T> 
    using enable_if_t = typename std::enable_if<P, T>::type; 

    // Mockup function signature to pick up the call when enable_if shunts 
    template <std::size_t N, class T = void> 
    void callMyFunc(T&&, ...) { 
     assert(!"Index not in range !"); 
    } 

    // "Simple" recursive solution, removes itself from the overload set 
    // to stop recursion 
    template <std::size_t N, class... Ts, 
     class = enable_if_t<N < sizeof...(Ts), void>> 
    void callMyFunc(std::tuple<Ts...> &tuple, std::size_t n) { 
     return n == N 
      ? my_function(std::get<N>(tuple)) 
      : callMyFunc<N + 1>(tuple, n); 
    } 
} 

// Tiny user-friendly wrapper 
template <class... Ts> 
void callMyFunc(std::tuple<Ts...> &tuple, std::size_t n) { 
    detail::callMyFunc<0u>(tuple, n); 
} 

int main(int, char**) { 
    std::tuple <int, char> my_tuple(3, 'q'); 

    // Success. 
    callMyFunc(my_tuple, 0u); 
    callMyFunc(my_tuple, 1u); 

    return 0; 
} 
2

以下可能會有所幫助:

template <typename T> struct caller; 

template <typename... Ts> struct caller<std::tuple<Ts...>> 
{ 
    template <typename F> 
    void operator() (F f, std::tuple<Ts...>& t, int n) 
    { 
     (*this)(f, t, n, std::index_sequence_for<Ts...>()); 
    } 
private: 
    template <typename F, std::size_t ... Is> 
    void operator() (F f, std::tuple<Ts...>& t, int n, std::index_sequence<Is...>) 
    { 
     std::function<void(F, std::tuple<Ts...>&)> fs[] = { &helper<F, Is>... }; 
     fs[n](f, t); 
    } 

    template <typename F, std::size_t I> 
    static void helper(F f, std::tuple<Ts...>& t) 
    { 
     f(std::get<I>(t)); 
    } 

}; 

template <typename F, typename T> 
void call(F f, T& t, int n) 
{ 
    caller<T>()(f, t, n); 
} 

Live example

+0

哈哈,我一開始就這樣,但我並不喜歡類型擦除。 – Quentin