2014-01-30 48 views
3

我正在構建一個稍微不對稱的類。在投訴進入之前,它必然是不對稱的。有一個轉換(需要一點時間的操作),當兩個對象加在一起時必須發生,並且轉換最自然地發生在正確的加法器上。是否有可能在C++中重載運算符關聯性?

爲了使這個具體的,這裏發生了什麼事的一般示例...

class Foo { 
    char _fav; 
    int _prop; 

public: 
    const char fav() const {return _fav;} 
    const int prop() const (return _prop;} 
    void changeFav(char); // complicated method that also changes _prop 
    void changeProp(int); // straightforward method 
} 

Foo 
operator + (Foo A, Foo B) { 
    Foo sum; 
    if (A.fav() != B.fav()) A.changeFav(B.fav); 
    sum.changeProp(A.prop() + B.prop()); 
    return sum; 
} 

爲了兩個Foo s到加入,他們需要有相同的_fav,所以選擇一定要作出哪些轉換。根據Foo的詳細信息,將左加數更改爲匹配正確的加數是很自然的。

但是,這樣做的時候:

Foo A,B,C; 
Foo D = A + B + C; // D = (A + B) + C 
Foo E = A + (B + C); 

如果A已經有相同的_favC,然後changeFavD調用兩次(一次更改A._favB._fav然後再次改變(A+B)._favC._fav)和一次用於E(將B._fav更改爲C._fav)。我更喜歡後者,但希望避免強制用戶使用括號來添加多個內容。

有沒有辦法超載operator +的關聯性,使這種情況發生?

+0

你的'operator +'不返回任何東西,並且兩個成員函數都缺少返回類型。 –

+1

不能提供完整的答案,但總而言之,不要返回foo,而是要使foo以外的其他操作數成爲正確的操作數。這只是我自己輸入的意識流。 –

+0

@LightnessRacesinOrbit是的,因爲這只是一個虛擬的例子,所以你可以看到發生了什麼。這不是真正的代碼。 – PengOne

回答

2

在C++標準條款5,

重載操作服從在條款5.

where子句5指定操作者優先級和結合指定的語法規則。

+0

謝謝。除非有好的破解,否則我會接受。 – PengOne

2

不需要。爲什麼不改變右側操作數的偏好?

+0

這很複雜,並且涉及(真正的)'Foo'的技術細節以及''Foo'的'+'含義。你能否詳細說明「否」?這是寫在石頭上,還是你從來沒有見過它? – PengOne

+2

@PengOne:如果你的問題是「我可以改變'+'的關聯性」,那麼答案是「你不能」。這不僅僅是我「從來沒有見過」:這是語言語法的一個事實。 –

+2

@LightnessRacesinOrbit:然而,如果有更多的代碼,就可以讓「A + B」的行爲好像它具有相反的關聯性,這幾乎是相同的結果。 –

3

你可以做一個破解。您必須使用另一種類型來保存操作的中間結果,然後可以使用隱式轉換來評估結果。這是如何實現的Python風格比較運營商在C++的例子:

#include <vector> 
#include <cstdio> 

struct S { 
    S(int x) : val(x) { } 
    int val; 
}; 

struct Comparison { 
    std::vector<S> operands; 
    explicit Comparison(S x) 
    { 
     operands.push_back(x); 
    } 
    operator S() 
    { 
     auto i = operands.begin(), e = operands.end(); 
     S prev = *i; 
     for (i++; i != e; i++) { 
      S cur = *i; 
      if (prev.val >= cur.val) 
       return S(0); 
      prev = cur; 
     } 
     return S(1); 
    } 
    void append(const Comparison &a) 
    { 
     operands.insert(
      operands.end(), 
      a.operands.begin(), 
      a.operands.end()); 
    } 
    void append(const S &a) 
    { 
     operands.push_back(a); 
    } 
}; 

Comparison operator<(const Comparison &left, const Comparison &right) 
{ Comparison result(left); result.append(right); return result; } 
Comparison operator<(const Comparison &left, const S &right) 
{ Comparison result(left); result.append(right); return result; } 
Comparison operator<(const S &left, const Comparison &right) 
{ Comparison result(left); result.append(right); return result; } 
Comparison operator<(const S &left, const S &right) 
{ Comparison result(left); result.append(right); return result; } 

int main() 
{ 
    S x(0); 
    x = S(0) < S(1) < S(2) < S(3); 
    std::printf("0 < 1 < 2 < 3 = %d\n", x.val); 
    x = S(0) < S(1) < S(3) < S(2); 
    std::printf("0 < 1 < 3 < 2 = %d\n", x.val); 
    return 0; 
} 

在這種情況下,但是,我會很快溝+操作。我會避免使用+進行任何不關聯和交換的操作,因爲這是數學中+的約定。相反,您可以使用可變參數函數(使用模板)執行所需的計算。

+0

我喜歡這種精神。關於'+',當在數學中添加兩個線性變換時,它們必須首先在相同的基礎上表達,這與我的示例非常相似。 – PengOne

+0

@PengOne:無需使用基礎來表達線性變換來添加它們 - 無論如何,並非所有線性變換都可以用基礎來表示。你可以定義「(f + g)(x)= f(x)+ g(x)」。 –

+0

是的,但我正在說明,如果'_fav'是選擇的基礎,'_prop'是基於此的矩陣表示,我的示例就很自然。 – PengOne

3

有點兒,但你不會喜歡它的「如何」。

首先,您需要閱讀並瞭解Boost.Proto文檔。然後,您需要弄清楚如何轉換所有表達式樹來顛倒操作的順序。然後,您需要對錶達樹的評估對最終用戶透明。可能在分配?我並沒有與Proto混爲一談,但類似於this article on Proto-based optimizations可能是一個有用的開始。