2017-07-06 121 views
-1

我有一個需要向量化輸入的函數。爲簡單起見,讓我們用這個print_in_order功能:幾乎將struct的向量轉換爲struct成員的向量

#include <iostream> 
#include <vector> 

template <typename vectorlike> 
void print_in_order(std::vector<int> const & order, 
        vectorlike const & printme) { 
    for (int i : order) 
     std::cout << printme[i] << std::endl; 
} 

int main() { 
    std::vector<int> printme = {100, 200, 300}; 
    std::vector<int> order = {2,0,1}; 
    print_in_order(order, printme); 
} 

現在我有一個vector<Elem>,想在矢量打印一個整數成員,Elem.a,每個ELEM。我可以通過創建一個新的vector<int>(複製所有元素的a)並將其傳遞給打印函數來實現 - 但是,我覺得必須有一種方法來傳遞一個「虛擬」向量,當使用operator[]時,退貨僅限會員a。請注意,我不想更改print_in_order函數來訪問成員,它應該保持一般。

這是可能的,也許與lambda表達式? 下面的完整代碼。

#include <iostream> 
#include <vector> 

struct Elem { 
    int a,b; 
    Elem(int a, int b) : a(a),b(b) {} 
}; 

template <typename vectorlike> 
void print_in_order(std::vector<int> const & order, 
        vectorlike const & printme) { 
    for (int i : order) 
     std::cout << printme[i] << std::endl; 
} 

int main() { 
    std::vector<Elem> printme = {Elem(1,100), Elem(2,200), Elem(3,300)}; 
    std::vector<int> order = {2,0,1}; 

    // how to do this? 
    virtual_vector X(printme) // behaves like a std::vector<Elem.a> 
    print_in_order(order, X); 
} 
+3

寫自己的類,它包裝一個向量和重載'[]'運營商? – imreal

回答

0

使用成員指針可以實現代理類型,通過它可以查看一個對象的容器,通過它的一個成員(請參閱pointer to data member)或其中一個獲得者(請參閱pointer to member function)代替每個對象。第一種解決方案只涉及數據成員,而第二種解決方案則涉及兩者

容器必須知道使用哪個容器以及要映射哪個成員,哪些成員將在施工時提供。指向成員的指針的類型取決於該成員的類型,因此它必須被視爲一個附加的模板參數。

template<class Container, class MemberPtr> 
class virtual_vector 
{ 
public: 
    virtual_vector(const Container & p_container, MemberPtr p_member_ptr) : 
     m_container(&p_container), 
     m_member(p_member_ptr) 
    {} 

private: 
    const Container * m_container; 
    MemberPtr m_member; 
}; 

接下來,實施operator[]操作,既然你提到這是你如何想訪問你的元素。首先解除引用成員指針的語法可能會令人驚訝。

template<class Container, class MemberPtr> 
class virtual_vector 
{ 
public: 
    virtual_vector(const Container & p_container, MemberPtr p_member_ptr) : 
     m_container(&p_container), 
     m_member(p_member_ptr) 
    {} 

    // Dispatch to the right get method 
    auto operator[](const size_t p_index) const 
    { 
     return (*m_container)[p_index].*m_member; 
    } 

private: 
    const Container * m_container; 
    MemberPtr m_member; 
}; 

要使用這個實現,你會寫是這樣的:

int main() { 
    std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) }; 
    std::vector<int> order = { 2,0,1 }; 

    virtual_vector<decltype(printme), decltype(&Elem::a)> X(printme, &Elem::a); 
    print_in_order(order, X); 
} 

這是一個有點麻煩,因爲沒有模板參數推導發生。因此,讓我們添加一個免費函數來推斷模板參數。

template<class Container, class MemberPtr> 
virtual_vector<Container, MemberPtr> 
make_virtual_vector(const Container & p_container, MemberPtr p_member_ptr) 
{ 
    return{ p_container, p_member_ptr }; 
} 

的使用變爲:

int main() { 
    std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) }; 
    std::vector<int> order = { 2,0,1 }; 

    auto X = make_virtual_vector(printme, &Elem::a); 
    print_in_order(order, X); 
} 

如果你想支持成員函數,這是一個稍微複雜一些。首先,解引用數據成員指針的語法與調用函數成員指針略有不同。您必須實現operator[]的兩個版本,並根據成員指針類型啓用正確的版本。幸運的是,該標準提供了std::enable_ifstd::is_member_function_pointer(均在<type_trait>標題中),這允許我們做到這一點。成員函數指針要求你指定要傳遞給函數的參數(在本例中爲非),以及表達式周圍的額外括號,這些括號會評估爲要調用的函數(參數列表之前的所有內容)。

template<class Container, class MemberPtr> 
class virtual_vector 
{ 
public: 
    virtual_vector(const Container & p_container, MemberPtr p_member_ptr) : 
     m_container(&p_container), 
     m_member(p_member_ptr) 
    {} 

    // For mapping to a method 
    template<class T = MemberPtr> 
    auto operator[](std::enable_if_t<std::is_member_function_pointer<T>::value == true, const size_t> p_index) const 
    { 
     return ((*m_container)[p_index].*m_member)(); 
    } 

    // For mapping to a member 
    template<class T = MemberPtr> 
    auto operator[](std::enable_if_t<std::is_member_function_pointer<T>::value == false, const size_t> p_index) const 
    { 
     return (*m_container)[p_index].*m_member; 
    } 

private: 
    const Container * m_container; 
    MemberPtr m_member; 
}; 

爲了測試這一點,我添加了一個getter到Elem類,用於說明目的。

struct Elem { 
    int a, b; 
    int foo() const { return a; } 
    Elem(int a, int b) : a(a), b(b) {} 
}; 

這裏是它如何被使用:

int main() { 
    std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) }; 
    std::vector<int> order = { 2,0,1 }; 

    { // print member 
     auto X = make_virtual_vector(printme, &Elem::a); 
     print_in_order(order, X); 
    } 
    { // print method 
     auto X = make_virtual_vector(printme, &Elem::foo); 
     print_in_order(order, X); 
    } 
} 
+0

謝謝,這就是我一直在尋找的東西。我也意識到似乎沒有單線解決方案。 – sssascha

3

根本不可能直接做你想做的事。相反,您可能需要從standard algorithm library中獲取提示,例如std::for_each,您可以參考多餘的參數,該參數是您爲每個元素調用的函數式對象。然後你可以輕鬆地傳遞一個只打印想要的元素的lambda函數。

也許類似

template<typename vectorlike, typename functionlike> 
void print_in_order(std::vector<int> const & order, 
       vectorlike const & printme, 
       functionlike func) { 
    for (int i : order) 
     func(printme[i]); 
} 

然後調用它像

print_in_order(order, printme, [](Elem const& elem) { 
    std::cout << elem.a; 
}); 

由於C++有重載函數,你仍然可以保留舊print_in_order功能對於普通的載體。現在

+2

您可以使用成員指針直接執行此操作。 –

+2

既然你的函數是完全一般的,可能不應該再叫'print_in_order',也許'for_each_in_order'或'apply_in_order'。 –

+0

你可以重新安排它仍然是'print_in_order':'[](Elem const&elem) - > Elem :: a const&{return elem.a; }'和'for(int i:order)std :: cout << func(printme [i]);' – Caleth

-1
template<class F> 
struct index_fake_t{ 
    F f; 
    decltype(auto) operator[](std::size_t i)const{ 
    return f(i); 
    } 
}; 
template<class F> 
index_fake_t<F> index_fake(F f){ 
    return{std::move(f)}; 
} 
template<class F> 
auto reindexer(F f){ 
    return [f=std::move(f)](auto&& v)mutable{ 
    return index_fake([f=std::move(f),&v](auto i)->decltype(auto){ 
     return v[f(i)]; 
    }); 
    }; 
} 
template<class F> 
auto indexer_mapper(F f){ 
    return [f=std::move(f)](auto&& v)mutable{ 
    return index_fake([f=std::move(f),&v](auto i)->decltype(auto){ 
     return f(v[i]); 
    }); 
    }; 
} 

,打印爲了可以改寫爲:

template <typename vectorlike> 
void print(vectorlike const & printme) { 
    for (auto&& x:printme) 
    std::cout << x << std::endl; 
} 
template <typename vectorlike> 
void print_in_order(std::vector<int> const& reorder, vectorlike const & printme) { 
    print(reindexer([&](auto i){return reorder[i];})(printme)); 
} 

並打印.a爲:

print_in_order(reorder, indexer_mapper([](auto&&x){return x.a;})(printme)); 

可能會有一些錯別字。

0

你有兩個數據結構

struct Employee 
{ 
    std::string name; 
    double salary; 
    long payrollid; 
}; 

std::vector<Employee> employees; 

或可替代

struct Employees 
{ 
    std::vector<std::string> names; 
    std::vector<double> salaries; 
    std::vector<long> payrollids; 
}; 

C++設計了第一個選項爲默認的選擇。其他語言如Javascript傾向於鼓勵第二種選擇。

如果你想找到平均工資,選項2更方便。如果您想通過薪水對員工進行排序,則選項1更容易處理。

但是,您可以使用lamdas在兩者之間進行部分相互轉換。 lambda是一個微不足道的小函數,它接受一個Employee併爲他返回一份薪水 - 這樣可以有效地提供一個雙倍的平面向量,我們可以採取這種方式 - 或者採用索引和Employees並返回僱員,做一點點簡單的數據重新格式化。

+0

這似乎沒有回答這個問題。 –

相關問題