2011-07-21 42 views
2

我正在玩Phoenix v3試圖弄清楚我們是否應該標準化而不是當前的綁定和lambda混合。從文檔中我得到的印象是應該可以簡化一些表達式。使用Boost.Phoenix的操作符 - > *

目前,我被困在與STL算法結合使用 - > *運算符。下面將編譯(Visual Studio 2008的SP1),但沒有給出預期輸出:

#include <algorithm> 
#include <iostream> 
#include <vector> 
#include <boost/mem_fn.hpp> 
#include <boost/phoenix.hpp> 
using namespace std; 
using namespace boost; 
using namespace boost::phoenix; 
using namespace boost::phoenix::arg_names; 

struct A 
{ 
    void f() const { cout << "A"; }; 
}; 
struct B 
{ 
    A a() { return A(); }; 
}; 

int main() 
{ 
    vector<A> va(1); 
    for_each(va.begin(), va.end(), bind(mem_fn(&A::f), arg1)); 
    for_each(va.begin(), va.end(), arg1->*&A::f); 

    vector<B> vb(1); 
    for_each(vb.begin(), vb.end(), bind(mem_fn(&A::f), bind(mem_fn(&B::a), arg1))); 
    return 0; 
} 

運行這個例子會打印出「A」兩次,兩次都爲基於BIND的循環。 所以這裏是我的問題:

  • 我應該改變,以便讓基於運算符的循環實際調用A :: f?
  • 我該如何使用運算符更改雙重綁定循環?
  • 任何人都知道爲什麼VS2008總是抱怨,當你沒有在這些情況下指定mem_fn?我總是得到警告C4180(適用於函數類型的限定符沒有意義;忽略)。

在此先感謝您的任何見解。

+0

警告C4180是良性的 - 忽略它在代碼中不是你的。 – ildjarn

回答

1

我也不太擅長鳳凰,但我認爲你不能按照你想要的方式使用 - > *操作符。
如果你改變你的例子來

... 
    vector<A*> va; 
    va.push_back(new A); 
    for_each(va.begin(), va.end(), bind(mem_fn(&A::f), arg1)); 
    for_each(va.begin(), va.end(), (arg1->*&A::f)()); 
... 

你會得到兩次A.在這些例子中,我只發現了指針的例子,所以我想你只能用鳳凰 - > *運算符與指針。應該可以,因爲運算符 - > *綁定到指針。
從規格在5.5:

的二進制運算符 - > *結合其第二個操作數,這應是 類型的「指針至T的成員」(其中,T是一個完全定義的類 型)到其第一操作數,它應是類型的「指針T」或 「指針到一類的T是一個明確的和可訪問的基 類

+0

是的,這是有效的!現在我也知道如何用值來做到這一點:(&arg1 - > *&A :: f)() –

+0

並且雙重綁定可以替換爲:(&((&arg1 - > *&B :: a)()) - > *&A :: f)(),並不是我認爲它更清晰...... –

+0

@Ben:不,這會調用UB,除非您使用VC++進行編譯,這允許將其作爲非標準的編譯器擴展。我會發佈一個完整的答案和更多細節。 – ildjarn

4

鑑於:

#include <algorithm> 
#include <vector> 
#include <iostream> 
#include <boost/phoenix.hpp> 

struct A 
{ 
    void f() const { std::cout << "A\n"; }; 
}; 

struct B 
{ 
    A a() const { return A(); }; 
}; 

第一個是一個簡單的辦法:

int main() 
{ 
    using boost::phoenix::arg_names::arg1; 

    std::vector<A> va(1); 
    std::for_each(va.begin(), va.end(), (&arg1->*&A::f)()); 
} 

關於operator->*,鳳凰文檔clearly state

構件指針操作者的左手側必須是一個演員返回指針類型。

因此,給定的對象(在這種情況下的A&)時,一個必須考慮所述目標的地址,以便使用operator->* - 因此&arg1。 (另外,因爲鳳凰演員是懶惰的,所以必須使用一組額外的括號才能獲得渴望的函子而不是懶惰的函子。)


第二個是不是很容易,因爲人們不能只使用運營商 - 因爲我們必須有代表才能使用operator->*一個指針演員,我們需要採取的地址B::a的結果,但B::a的結果是一個右值,並且取任何右值的地址都是非法的。我們有兩個選擇:

  1. 商店B::a結果到一個變量,使它成爲一個左值,從而使得它的法律採取的地址。這可以使用phoenix::let

    int main() 
    { 
        using boost::phoenix::let; 
        using boost::phoenix::arg_names::arg1; 
        using boost::phoenix::local_names::_a; 
    
        std::vector<B> vb(1); 
        std::for_each(
         vb.begin(), 
         vb.end(), 
         (let(_a = (&arg1->*&B::a)())[(&_a->*&A::f)()]) 
        ); 
    } 
    

    很明顯,這是非常醜陋的。

  2. 的使用phoenix::bind代替operator->*,因爲它效果一樣好引用和指針,避免了需要採取的B::a結果的地址:

    int main() 
    { 
        using boost::phoenix::bind; 
        using boost::phoenix::arg_names::arg1; 
    
        std::vector<B> vb(1); 
        std::for_each(vb.begin(), vb.end(), bind(&A::f, (&arg1->*&B::a)())); 
    } 
    
+0

感謝您的詳細解釋。看起來我正要被VC++的'特徵'再次咬傷。如果不知道,我們的測試編譯器(GCC和ICC)將不得不趕上它。回顧我的問題,我實際上認爲 - > *操作符可以用作方便地替換綁定。似乎我錯了,我想我會繼續使用(鳳凰)綁定這種東西。我不想擔心迭代一組值或指針。 –

+0

+1爲真正詳細的答案。這幫助我更好地理解鳳凰。 – mkaes