簡體   English   中英

C++0x 中高階函數和 lambda 的問題

[英]A problem with higher order functions and lambdas in C++0x

我有一個程序,在對每個組件進行一些計算后,我必須在屏幕上打印許多 STL 向量。 所以我嘗試像這樣創建一個 function :

template <typename a> 
void printWith(vector<a> foo, a func(a)){
  for_each(foo.begin(), foo.end(), [func](a x){cout << func(x) << " "; });
}

然后像這樣使用它:

int main(){
  vector<int> foo(4,0);
  printWith(foo, [](int x) {return x + 1;});
  return 0;
}

不幸的是,我在printWith調用中遇到了關於 lambda 表達式類型的編譯錯誤:

g++ -std=gnu++0x -Wall -c vectest.cpp -o vectest.o
vectest.cpp: In function ‘int main()’:
vectest.cpp:16:41: error: no matching function for call to ‘printWith(std::vector<int>&, main()::<lambda(int)>)’
vectest.cpp:10:6: note: candidate is: void printWith()
make: *** [vectest.o] Error 1

當然,如果我這樣做:

int sumOne(int x) {return x+1;}

然后printWith(foo, sumOne); 按預期工作。 我認為 lambda 表達式的類型將是具有推斷返回類型的 function 的類型。 我也認為我可以在任何可以安裝普通 function 的地方安裝 lambda。 我該如何進行這項工作?

以下對我有用:

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

template <typename a, typename F>
void printWith(vector<a> foo, F f){
  for_each(foo.begin(), foo.end(), [&](a x){cout << f(x) << " "; });
}

int main(){
  vector<int> foo = {1,2,3,4,5};
  printWith(foo, [](int x) {return x + 1;});
  std::cout << '\n';
  return 0;
}

測試:

$ g++-4.5 -std=gnu++0x -Wall test.cpp
$ ./a.out                            
2 3 4 5 6 

或者,您可以利用沒有 lambda 捕獲的閉包類型可以隱式轉換為 function 指針的事實。 This is closer to your original code and also cuts down on the number of instantiations of the function template (in the original solution you get a new instantiation every time you use the function template with a different function object type; note though that it doesn'在這種特定情況下無關緊要,因為printWith function 非常短,很可能總是內聯):

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

template <typename a, typename b>
void printWith(const vector<a>& foo, b f(a)){
  for_each(foo.begin(), foo.end(), [=](a x){cout << f(x) << " "; });
}

int main(){
  vector<int> foo = {1,2,3,4,5};
  printWith<int, int>(foo, [](int x) {return x + 1;});
  std::cout << '\n';
  return 0;
}

不幸的是,隱式轉換在模板參數推導中表現不佳:如您所見,我必須在調用 printWith 時指定模板printWith

另一種選擇是使用std::function 這也有助於最大限度地減少模板實例化的數量,甚至適用於具有 lambda-capture 的 lambda 表達式,但在模板參數推導方面存在相同的問題:

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

using namespace std;

template <typename a, typename b>
void printWith(const vector<a>& foo, std::function<b(a)> f){
  for_each(foo.begin(), foo.end(), [&](a x){cout << f(x) << " "; });
}

int main(){
  vector<int> foo = {1,2,3,4,5};
  int y = 1;
  printWith<int, int>(foo, [&](int x) { return x + y; });
  std::cout << '\n';
  return 0;
}

您遇到問題的原因是因為您嘗試使用 function。 自由函數具有特定的表示(作為 function 指針),它不能與任何類型的 function 對象互換。 應該避免使用 Function 指針(基本上就是你在這里所擁有的)。 您需要使用模板指定的類型直接獲取 function object。

template <typename a, typename Func> 
void printWith(vector<a> foo, Func func){
  for_each(foo.begin(), foo.end(), [&](a x){cout << func(x) << " "; });
}

或者,采用多態 function object 例如std::function

template<typename a>
void printWith(vector<a> foo, std::function<string(const a&)> func) {
    for_each(foo.begin(), foo.end(), [&](a x) { cout << func(x) << " "; });
}
void printWith(vector<a> foo, b func(a)){

這是錯誤的,你不能這樣做,這會使編譯器不考慮這段代碼,因為它是無效的。

您有兩種方法可以解決此問題:

1)不要求參數類型,只要求函子:

   void printWith(vector<a> foo, b func ){ // keep the rest of the code the same

如果 func 不采用a作為參數,則 function 的 rest 將無法編譯。

2)強制函子類型:

template <typename a> 
void printWith(vector<a> foo, std::function< void (a) > func ){

然后就像您使用的是 function 指針一樣。 沒有(或更少)編譯時優化,但至少您強制執行函子簽名。 有關詳細信息,請參見 std::function 或 boost::function。

這不起作用的原因是您將模板參數推導與隱式轉換混合在一起。 如果你擺脫扣除它的工作原理:

printWith<int>(foo, [](int x) {return x + 1;});

但是,最好(在 printWith 內部)讓func的類型成為另一個模板參數,正如其他人所推薦的那樣。

另一方面,如果您真的想為這種類型添加約束,有更好的方法可以使用 SFINAE(用於軟錯誤)或static_assert (用於硬錯誤)。

例如:

// A constraints metafunction
template<typename T, typename Element>
struct is_element_printer
    : std::is_convertible<T, Element (*)(Element)>
{};

在這里, is_element_printer<T, Element>::valuetrue ,當且僅當T隱式轉換為Element (*)(Element) 我僅將其用於說明目的,我不推薦將其用於實際使用:在很多情況下,有很多東西可以稱為“元素打印機”,而不是 function 指針。 我這樣做只是因為std::is_convertible很容易從<type_traits>獲得,並且沒有其他更明顯的測試可用。 你應該自己寫。

然后:

template<typename Container, typename Functor>
void
printWith(Container&& container, Functor&& functor)
{
    // avoid repetition
    typedef typename std::decay<Container>::type::value_type value_type;

    // Check our constraints here
    static_assert(
        std::is_element_printer<
            typename std::decay<Functor>::type,
            value_type
        >::value,
        "Descriptive error message here"
    );

    // A range-for is possible instead
    std::for_each(container.cbegin(), container.cend(), [&functor](value_type const& v)
    { std::cout << functor(v) << ' '; });
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM