2013-03-22 33 views
3

我正在寫一些時髦的音頻代碼,並嘗試使用運算符重載來創建一個非常乾淨和簡單的API。它變成了一個C++的謎題...「分配到索引」C++操作符重載謎題

我想通過一個「分配到索引」排序的複合運算符立即解決我很肯定不存在。任何人都可以對以下是否有可能有任何見解?

我有2種對象類型....

Frames frames; // audio data, contains 1 buffer (float *) for each channel 
Sample s;  // a single sample, 1 float for each channel 

所以Sample是一種幀的正交切片,即幀是不是Sample的陣列。如果您知道音頻,Frames是「非交錯」,並且Sample是。

聖盃...

s = frames[1]; // statement 1. gets a copy of values in frame 1 
frames[1] = s; // statement 2. replace values in frame 1 with those in Sample s 

第一個是沒有問題的:

// in class Frames... 
Sample& operator[](size_t idx) const { 
    Sample s; 
    s.left = _leftCh[idx]; 
    s.right = _rightCh[idx]; 
    return s; 
} 

但第二個任務是棘手,因爲上面的函數創建數據的副本,而不是參考。

我試着定義與參考樣本...

class Sample { 
public: 
    float& L; 
    float& R; 
    Sample(float& lt, float& rt) : L(lt), R(rt) {}; 
} 

但你不能做的簡單的東西...

Sample s(0.0, 0.0); 
s.left = 0.2; 

另一個潛在的解決方案是有這兩個語句調用兩個不同的運算符重載。然後執行該語句2調用此[]過載,它返回一個新的框架對象指向值,而不是一個Sample對象:

Frames& operator[](size_t idx) { 
    // Construct an new Frames object whose which 
    // points to value idx in each channel 
    Frames f(_size - idx); 
    f._leftCh = &_leftCh[idx]; 
    f._rightCh = &_rightCh[idx]; 
    return f; 
} 

,然後添加一個賦值操作符來Frames剛剛替換第一個值...

Frames& operator=(const Sample& s) { 
    _leftCh[0] = s.left; 
    _rightCh[0] = s.right; 
    return *this; 
} 

編譯器告訴我,方法必須由不僅僅是返回類型更加不同,但這是爲operator[]重載一個方法名稱後有const解決。這裏可能有線索嗎?有沒有辦法讓陳述1致電Sample& operator[]...和陳述2致電Frames& operator[]...。還是有更好的方法來完成這個?

感謝您的耐心,如果您已經做到了這一點!非常感謝......

+0

第一個** **是一個問題,因爲你是返回一個引用局部變量... – 2013-03-22 09:22:26

+0

第一個是錯誤的:要返回,當你退出該模具的局部變量的引用操作員的範圍。 – juanchopanza 2013-03-22 09:24:14

+0

是的,我也這麼認爲,但是XCode/LLVM在其他情況下不會給我一個警告。這可能是因爲引用立即被複制或可能,因爲我已經定義了內聯方法。似乎很奇怪,但它迄今爲止工作。 – 2013-03-23 18:09:21

回答

5

如何:

class SampleRef { 
    float &left_, &right_; 

public: 
    SampleRef(float& left, float& right) 
    : left_(left), right_(right) 
    {} 

    operator Sample() { 
    return Sample(left_, right_); 
    } 

    SampleRef& operator= (const Sample &arg) { 
    left_ = arg.left; 
    right_ = arg.right; 
    return *this 
    } 
}; 

SampleRef Frames::operator[] (size_t idx) { 
    return SampleRef(_leftCh[idx], _rightCh[idx]); 
} 

你當然也可以添加const超載operator[]這將簡單地返回一個Sample

Sample Frames::operator[] (size_t idx) const { 
    return Sample(_leftCh[idx], _rightCh[idx]); 
} 
+2

不錯! 「每一個編程問題都可以通過額外的間接方式來解決,除了太多的間接層次!」。 – rodrigo 2013-03-22 09:29:13

+0

我想知道賦值運算符的語義是否真的是OP的意圖。 – juanchopanza 2013-03-22 09:32:52

+0

@juanchopanza我相信如此。引用OP的「Holy Grail」:'frames [1] = s;'「將第1幀中的值替換爲Sample's'」中的值。 – Angew 2013-03-22 09:45:34

0

你試過沒有超載只是爲了弄清楚細節?例如sample = frame.getSample(); frame.setSample(sample);一旦細節達到您的滿意,那麼您可以添加語法糖,並使運算符[]=過載。

它看起來像你想保持原來的樣品的引用,使得例如:

sample.right = oldR; 
sample.left = oldL; 
f[x] = sample; 

sample.right = newR; 
sample.left = newL; 
newSample = f[x]; 

assert(sample.right == newSample.right && sample.left == newSample.left); 

這是正確的嗎?如果是這樣,我認爲你不能這樣做,因爲你「打破」你的樣本將其插入框架,所以你失去了原來的聯繫。

+0

不完全。我不希望用'f [x] = sample''將'sample'鏈接到''f [x]',只將'sample'中的值賦給'f'幀內容的內部緩衝區'x' 。在你的例子中,'newSample.right'應該等於'oldR','sample'只是一個棧值,其值被複制或賦值給'f [x]'。 – 2013-03-23 10:48:51

+0

另外,爲什麼我沒有去的方法先當時的語法糖途徑(儘管往往是一個很好的做法)是,我開始相信運營商基於API的要求,我堂堂只允許一個非常特殊的原因對象模型可能不是您從第一原則中選擇的模型。值得一提的是,如果C++支持屬性和訪問器,那麼可能會避免整個複雜度。我試圖避免最終API中的過度冗長和括號,例如'frames-> left()[5]'或'frames-> set(sample-> left(),index)' – 2013-03-23 10:49:22