C++ valarray物件適用運算子詳解

2020-07-16 10:04:30
這裡有 4 個可以應用到 valarray 物件的一元運算子:+、-、~ 和 !。效果是將運算子應用到陣列的每個元素上,並返回一個新的 valarray 物件作為結果,不改變原物件。只能將它們應用到元素型別支援這些運算子的 valarray 物件上應用!運算子得到的新元素總為布林型別,所以這個運算的結果是一個 valarray<bool>型別的物件。本章的後面會討論它的使用場景。

其他運算子生成的結果必須和這個運算的合法原始元素是相同的型別。例如,一元減法運算子只能反轉有符號數的元素的符號,因此它無法用於無符號型別。下面的程式碼展示了 ! 運算子的效果:
std::valarray<int> data {2, 0, -2, 4, -4};
auto result = !data; //result is of type valarray bool
std::copy(std::begin(result) , std::end(result),std::ostream_iterator<bool>{std::cout<<std::boolalpha, " "});
std::cout << std::endl;
// Output: false true false false false
當 ! 被應用到 data 中的值時,值首先會被隱式轉換為布林,然後運算子會被應用到結果上。如果想用 copy() 演算法以布林值的形式輸出值,結果肯定是 true false true true true,這解釋了為什麼上述程式碼的輸出是註釋顯示的那樣。

~ 運算子是位的 NOT (位的取反)或 1 的二補數。下面是一個範例:
std::valarray<int> data {2, 0, -2, 4, -4}; // 0x00000002 0 Oxfffffffe 0x00000004 Oxfffffffc
auto result = ~data;
std::copy(std::begin(result), std::end(result),std::ostream_iterator<int> {std::cout, " "});
std::cout << std::endl;
// Output: -3 -1 1 -5 3
為了生成結果中的元素,可以通過對原始整數值取反的方式來得到。例如,data 中的第二個元素的位元位全為 0,因此應用 ~ 產生的值的位元位全為 1,對應於十進位制的 -1。

+ 運算子對數值沒有效果;- 運算子會改變符號。例如:
std::valarray<int> data {2, 0, -2, 4, -4};
auto result = -data;
std::copy(std::begin(result), std::end(result),std::ostream_iterator<int> {std::cout," "});
std::cout << std::endl;
// Output: -2 0 2 -4 4
當然,也可以覆蓋原始物件。為了使程式碼不那麼雜亂,從現在起,假設為 std::valarray 使用的 using 指令生效,然後去掉程式碼中用於對 valarray 型別進行限定的 std 名稱空間。

用於valarray物件的複合賦值運算子

所有的複合賦值運算子的左運算元都是 valarray 物件,右運算元是一個和所儲存的元素同型別的值。在這種情況下,值會被合併到每個元素的值上,合併方式由運算子決定。右運算元也可以是和左運算元有相同元素個數和元素型別的 valarray 物件。在這種情況下,左運算元會因為合併了右運算元對應的元素而被修改。這一類的運算子有:

1) 複合算術賦值運算子 +=、-=、*=、/=、%=。例如:
valarray<int> v1 {1, 2, 3, 4};
valarray<int> v2 {3, 4, 3, 4};
v1+= 3; // v1 is: 4 5 6 7
v1 -= v2;   // v1 is: 1 1 3 3
2) 複合位元運算賦值運算子 &=、|=、A=。例如:
valarray<int> v1{1, 2, 4, 8};
valarray<int> v2 {4, 8, 16, 32};
v1 |= 4;    // v1 is: 5 6 4 12
v1 &= v2;   // v1 is: 4 0 0 0
v1 ^= v2;   // v1 is: 0 8 16 32
3) 複合移位賦值運算子 >>=、<<=。例如:
valarray<int> v1 {1, 2, 3, 4};
valarray<int> v2 {4, 8, 16, 32};
v2 <<= v1; // v2 is: 8 32 128 512
v2 >>= 2; // v1 is: 2 8 32 128
複合位元運算和複合移位運算子一般用於整數型別。

valarray物件的二元運算

可以將能夠應用到基本型別值的任何二元運算子應用到 valarray 物件上,也能夠合併兩個 valarray 物件的相應元素,或者將 valarray 中的元素和一個同型別的值合併。在 valarray 標頭檔案中定義了下面這些二元運算子的非成員操作符函數:
  • 算術運算子 +、-、*、/、%;
  • 位元運算運算子 &、|、^;
  • 位移運算子 >>、<<;
  • 邏輯運算子 &&、||;

這些運算子有不同的版本,可以應用到一個 valarmy<T> 物件和一個 T 型別物件上、一個 T 型別物件和一個 valarray 物件上,或者應用到兩個 valarray 物件上。兩個 valarray 物件上的運算需要它們有相同個數的相同型別的元素。邏輯運算子會返回一個和 valarray 運算元有相同個數元素的 valarray<bool> 物件。其他的運算子會返回一個和 valarray 運算元有相同型別且相同個數元素的 valarray 物件。

將 valarray 物件的內容輸出到 cout 來說明發生了什麼是很有用處的:
// perline is the number output per line, width is the field width
template<typename T>
void print(const std::valarray<T> values, size_t perline = 8, size_t width = 8)
{
    size_t n {};
    for (const auto& value : values)
    {
        std::cout << std::setw(width) << value << " "; if(++n % perline == 0) std::cout << std::endl;
    }
    if (n % perline != 0) std::cout << std::endl;
    std::cout << std::endl;
}
這個函數適用於包含任何 T 型別元素的 valarray 物件,只要 T 型別元素支援輸出流的 operator<<()。

這裡不會為所有的二元運算子都舉一個範例,只做少量說明。下面是一個將二元算術運算子用於 valarray 物件的範例:
valarray<int> even {2, 4, 6, 8};
valarray<int> odd {3, 5, 7, 9};
auto r1 = even + 2;
print(r1, 4, 3);    // r1 contains: 4 6 8 10
auto r2 = 2*r1 + odd;
print(r2, 4, 3);    // r2 contains: 11 17 23 29
r1 += 2*odd - 4* (r2 - even);
print(r1, 4, 3);    // r1 contains: -26 -36 -4 6 -56
最後一條語句使用複合賦值運算子的成員函數來加上右運算元(一個表示式)的結果。 這說明可以像數值那樣來合併包含 valarray 物件的運算,包括括號的使用。下面是一個使用位移運算的範例:
print (odd <<3, 4, 4) ; // Output is: 24 40 56 72
print() 的第一個引數是將 odd 中的元素向左移動 3 個位元位之後得到的 valarray 物件。 每行輸出 4 個值,每個值的寬度是 4。

valarray 標頭檔案中也定義了一些非成員函數,可以用來比較兩個 valarray<T> 物件,或者將 valarray<T> 物件的每一個元素和 T 型別的值做比較。比較的結果儲存在 valarray<bool> 物件中,它和 valarray 包含相同個數的元素。支援的運算是 ==、!=、<、<=、> 和 >=。下面是一些使用這些運算子的範例:
valarray<int> even {2, 4, 6, 8};
valarray<int> odd {3, 5, 7, 9};
std::cout << std::boolalpha;
print(even + 1 == odd, 4, 6);
//Output is: true true true true
auto result = (odd < 5) && (even +3 != odd);
print(result); //Output is: true false false false
倒數第二條語句用二元運算子 && 來合併比較結果。結果說明,當 odd 元素小於 5 時,even 中相應的元素加上 3 後不等於 odd 中的元素;只有在比較 even 和 odd 的第一個元素時表示式才為 true,因為 odd 中只有第一個元素能使 odd<5,能使 even + 3 != odd 總為 true。

有一些定義了適用於 valarray 的元素子集的輔助類。主要的輔助類是 std::slice 和 std::gslice。為這些程式碼去掉 std 名稱空間限定符。在更深入了解能用 valarray 做什麼之前,讓我們先探索如何將輔助類用於 valarray。