日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

C++?Boost?Phoenix庫示例分析使用_C 語言

作者:無水先生 ? 更新時間: 2022-12-12 編程語言

一、說明

在函數式編程模型中,函數是對象,與其他對象一樣,可以作為參數傳遞給函數或存儲在容器中。有許多支持函數式編程模型的 Boost 庫。

  • Boost.Phoenix 是這些庫中最廣泛、也是最重要的庫。它取代了庫 Boost.Lambda,它被簡要介紹,但只是為了完整性。
  • Boost.Function 提供了一個類,可以輕松定義函數指針,而無需使用源自 C 編程語言的語法。
  • Boost.Bind 是一個適配器,即使實際簽名與預期簽名不同,它也允許您將函數作為參數傳遞給其他函數。
  • Boost.Ref 可用于傳遞對對象的引用,即使函數通過副本傳遞參數。
  • Boost.Lambda 可以稱為 Boost.Phoenix 的前身。它是一個相當古老的庫,并且在將 C++11 添加到編程語言之前很多年就允許使用 lambda 函數。

二、預先知道Boost.Phoenix

Boost.Phoenix 是函數式編程最重要的 Boost 庫。雖然 Boost.Bind 或 Boost.Lambda 等庫為函數式編程提供了一些支持,但 Boost.Phoenix 包含這些庫的功能并超越了它們。

在函數式編程中,函數是對象,可以像對象一樣處理。使用 Boost.Phoenix,一個函數可以返回另一個函數作為結果。也可以將一個函數作為參數傳遞給另一個函數。因為函數是對象,所以可以區分實例化和執行。訪問一個函數不等于執行它。

Boost.Phoenix 支持使用函數對象進行函數式編程:函數是基于類的對象,這些類重載了運算符 operator()。這樣,函數對象的行為就像 C++ 中的其他對象一樣。例如,它們可以被復制并存儲在容器中。但是,它們的行為也類似于函數,因為它們可以被調用。

函數式編程在 C++ 中并不新鮮。您可以將一個函數作為參數傳遞給另一個函數,而無需使用 Boost.Phoenix。

三、示例和代碼

示例 39.1。謂詞作為全局函數、lambda 函數和 Phoenix 函數

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
bool is_odd(int i) { return i % 2 == 1; }
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  std::cout << std::count_if(v.begin(), v.end(), is_odd) << '\n';
  auto lambda = [](int i){ return i % 2 == 1; };
  std::cout << std::count_if(v.begin(), v.end(), lambda) << '\n';
  using namespace boost::phoenix::placeholders;
  auto phoenix = arg1 % 2 == 1;
  std::cout << std::count_if(v.begin(), v.end(), phoenix) << '\n';
}

Example39.1

示例 39.1 使用算法 std::count_if() 來計算向量 v 中的奇數。std::count_if() 被調用 3 次,一次使用謂詞作為獨立函數,一次使用 lambda 函數,一次使用鳳凰功能。

Phoenix 函數與獨立函數和 lambda 函數不同,因為它沒有框架。雖然其他兩個函數有一個帶有簽名的函數頭,但 Phoenix 函數似乎只包含一個函數體。

Phoenix 函數的關鍵組件是 boost::phoenix::placeholders::arg1。 arg1 是函數對象的全局實例。您可以像 std::cout 一樣使用它:一旦包含相應的頭文件,這些對象就會存在。

arg1 用于定義一元函數。表達式 arg1 % 2 == 1 創建一個需要一個參數的新函數。該函數不會立即執行,而是存儲在 Phoenix 中。 phoenix 被傳遞給 std::count_if() ,它為 v 中的每個數字調用謂詞。

arg1 是調用 Phoenix 函數時傳遞的值的占位符。由于此處僅使用了 arg1,因此創建了一個一元函數。 Boost.Phoenix 提供了額外的占位符,例如 boost::phoenix::placeholders::arg2 和 boost::phoenix::placeholders::arg3。 Phoenix 函數總是期望與具有最大數量的占位符一樣多的參數。

Example39.1

示例 39.1 將 3 寫入標準輸出 3 次。

示例 39.2。 Phoenix 函數與 lambda 函數

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  auto lambda = [](int i){ return i % 2 == 1; };
  std::cout << std::count_if(v.begin(), v.end(), lambda) << '\n';
  std::vector<long> v2;
  v2.insert(v2.begin(), v.begin(), v.end());
  using namespace boost::phoenix::placeholders;
  auto phoenix = arg1 % 2 == 1;
  std::cout << std::count_if(v.begin(), v.end(), phoenix) << '\n';
  std::cout << std::count_if(v2.begin(), v2.end(), phoenix) << '\n';
}

Example39.2

例 39.2 強調了 Phoenix 和 lambda 函數之間的一個關鍵區別。除了不需要帶有參數列表的函數頭之外,Phoenix 函數參數沒有類型。 lambda 函數 lambda 需要一個 int 類型的參數。 Phoenix 函數 phoenix 將接受模運算符可以處理的任何類型。

將 Phoenix 函數視為函數模板。像函數模板一樣,Phoenix 函數可以接受任何類型。這使得在示例 39.2 中可以使用 phoenix 作為容器 v 和 v2 的謂詞,即使它們存儲不同類型的數字。如果您嘗試將謂詞 lambda 與 v2 一起使用,則會出現編譯器錯誤。

示例 39.3。 Phoenix 用作延遲 C++ 代碼

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  using namespace boost::phoenix::placeholders;
  auto phoenix = arg1 > 2 && arg1 % 2 == 1;
  std::cout << std::count_if(v.begin(), v.end(), phoenix) << '\n';
}

Example39.3

示例 39.3 使用 Phoenix 函數作為謂詞,使用 std::count_if() 計算大于 2 的奇數。Phoenix 函數訪問 arg1 兩次:一次測試占位符是否大于 2,一次測試它是否為奇數.條件與 && 相關聯。

您可以將 Phoenix 函數視為不會立即執行的 C++ 代碼。示例 39.3 中的 Phoenix 函數看起來像一個使用多個邏輯和算術運算符的條件。但是,條件不會立即執行。它僅在從 std::count_if() 中訪問時執行。 std::count_if() 中的訪問是正常的函數調用。

示例 39.3 將 2 寫入標準輸出。

示例 39.4。顯式 Phoenix 類型

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  using namespace boost::phoenix;
  using namespace boost::phoenix::placeholders;
  auto phoenix = arg1 > val(2) && arg1 % val(2) == val(1);
  std::cout << std::count_if(v.begin(), v.end(), phoenix) << '\n';
}

Example39.4

例 39.4 對 Phoenix 函數中的所有操作數使用顯式類型。嚴格來說,你看不到類型,只有輔助函數 boost::phoenix::val()。此函數返回使用傳遞給 boost::phoenix::val() 的值初始化的函數對象。函數對象的實際類型無關緊要。重要的是 Boost.Phoenix 為不同類型重載了運算符,如 >、&&、% 和 ==。因此,不會立即檢查條件。相反,函數對象被組合以創建更強大的函數對象。根據操作數,它們可能會自動用作函數對象。否則,您可以調用 val() 等輔助函數。

示例 39.5。 boost::phoenix::placeholders::arg1 和 boost::phoenix::val()

#include <boost/phoenix/phoenix.hpp>
#include <iostream>
int main()
{
  using namespace boost::phoenix::placeholders;
  std::cout << arg1(1, 2, 3, 4, 5) << '\n';
  auto v = boost::phoenix::val(2);
  std::cout << v() << '\n';
}

Example39.5

示例 39.5 說明了 arg1 和 val() 如何工作。 arg1 是函數對象的實例。它可以直接使用,也可以像函數一樣調用。您可以傳遞任意數量的參數——arg1 返回第一個參數。

val() 是一個用于創建函數對象實例的函數。函數對象使用作為參數傳遞的值進行初始化。如果實例像函數一樣被訪問,則返回該值。

示例 39.5 將 1 和 2 寫入標準輸出。

示例 39.6。創建自己的 Phoenix 函數

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
struct is_odd_impl
{
    typedef bool result_type;
 
    template <typename T>
    bool operator()(T t) const { return t % 2 == 1; }
};
boost::phoenix::function<is_odd_impl> is_odd;
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  using namespace boost::phoenix::placeholders;
  std::cout << std::count_if(v.begin(), v.end(), is_odd(arg1)) << '\n';
}

Example39.6

示例 39.6 解釋了如何創建自己的 Phoenix 函數。您將函數對象傳遞給模板 boost::phoenix::function。該示例傳遞了 is_odd_impl 類。此類重載運算符 operator():當傳入奇數時,運算符返回 true。否則,運算符返回 false。

請注意,您必須定義類型result_type。 Boost.Phoenix 使用它來檢測運算符 operator() 的返回值的類型。

is_odd() 是一個可以像 val() 一樣使用的函數。兩個函數都返回一個函數對象。調用時,參數將轉發給運算符 operator()。對于示例 39.6,這意味著 std::count_if() 仍然計算奇數。

示例 39.7。將獨立功能轉換為 Phoenix 功能

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
bool is_odd_function(int i) { return i % 2 == 1; }
BOOST_PHOENIX_ADAPT_FUNCTION(bool, is_odd, is_odd_function, 1)
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  using namespace boost::phoenix::placeholders;
  std::cout << std::count_if(v.begin(), v.end(), is_odd(arg1)) << '\n';
}

如果要將獨立函數轉換為 Phoenix 函數,可以按照示例 39.7 進行操作。您不必像前面的示例中那樣定義函數對象。

您可以使用宏 BOOST_PHOENIX_ADAPT_FUNCTION 將獨立函數轉換為 Phoenix 函數。將返回值的類型、要定義的 Phoenix 函數的名稱、獨立函數的名稱以及參數個數傳遞給宏。

示例 39.8。 Phoenix 使用 boost::phoenix::bind() 函數

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
bool is_odd(int i) { return i % 2 == 1; }
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  using namespace boost::phoenix;
  using namespace boost::phoenix::placeholders;
  std::cout << std::count_if(v.begin(), v.end(), bind(is_odd, arg1)) << '\n';
}

要將獨立函數用作 Phoenix 函數,您還可以使用 boost::phoenix::bind(),如示例 39.8 中所示。 boost::phoenix::bind() 的工作方式類似于 std::bind()。獨立函數的名稱作為第一個參數傳遞。所有進一步的參數都被轉發到獨立功能。

小費
避免使用 boost::phoenix::bind()。創建您自己的 Phoenix 函數。這會導致代碼更具可讀性。尤其是對于復雜的表達式,處理 boost::phoenix::bind() 的額外細節是沒有幫助的。

示例 39.9。任意復雜的 Phoenix 函數

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  using namespace boost::phoenix;
  using namespace boost::phoenix::placeholders;
  int count = 0;
  std::for_each(v.begin(), v.end(), if_(arg1 > 2 && arg1 % 2 == 1)
    [
      ++ref(count)
    ]);
  std::cout << count << '\n';
}

Boost.Phoenix 提供了一些模擬 C++ 關鍵字的函數對象。例如,您可以使用函數 boost::phoenix::if_()(參見示例 39.9)創建一個函數對象,該對象的行為類似于 if 并測試條件。如果條件為真,則使用 operator[] 傳遞給函數對象的代碼將被執行。當然,該代碼也必須基于函數對象。這樣,您可以創建復雜的 Phoenix 函數。

示例 39.9 對每個大于 2 的奇數進行增量計數。要對 count 使用增量運算符,請使用 boost::phoenix::ref() 將 count 包裝在一個函數對象中。與 boost::phoenix::val() 相比,沒有值被復制到函數對象中。 boost::phoenix::ref() 返回的函數對象存儲了一個引用——這里是對 count 的引用。

小提示

不要使用 Boost.Phoenix 創建復雜的函數。最好使用 C++11 中的 lambda 函數。雖然 Boost.Phoenix 接近 C++ 語法,但使用 if_ 等關鍵字或方括號之間的代碼塊并不一定會提高可讀性。

原文鏈接:https://yamagota.blog.csdn.net/article/details/127702549

欄目分類
最近更新