Hàm lambda đệ quy trong C ++ 11


143

Tôi mới sử dụng C ++ 11. Tôi đang viết hàm lambda đệ quy sau, nhưng nó không biên dịch.

sum.cpp

#include <iostream>
#include <functional>

auto term = [](int a)->int {
  return a*a;
};

auto next = [](int a)->int {
  return ++a;
};

auto sum = [term,next,&sum](int a, int b)mutable ->int {
  if(a>b)
    return 0;
  else
    return term(a) + sum(next(a),b);
};

int main(){
  std::cout<<sum(1,10)<<std::endl;
  return 0;
}

dịch lỗi:

vimal @ linux-718q: ~ / Học / 09C ++ / c ++ 0x / lambda> g ++ -std = c ++ 0x sum.cpp

sum.cpp: Trong hàm lambda: sum.cpp: 18: 36: error: ' ((<lambda(int, int)>*)this)-><lambda(int, int)>::sum' không thể được sử dụng làm hàm

phiên bản gcc

phiên bản gcc 4.5.0 20091231 (thử nghiệm) (GCC)

Nhưng nếu tôi thay đổi khai báo sum()như bên dưới, nó hoạt động:

std::function<int(int,int)> sum = [term,next,&sum](int a, int b)->int {
   if(a>b)
     return 0;
   else
     return term(a) + sum(next(a),b);
};

Ai đó có thể xin vui lòng ném ánh sáng này?


Điều này có thể là tĩnh so với khai báo động?
Hamish Grubijan

3
Là gì mutabletừ khóa làm gì ở đó?
Chúc mừng và hth. - Alf

Không được phép chụp các biến có thời lượng lưu trữ không tự động. Bạn nên làm theo cách này: chat.stackoverflow.com/transcript/message/39298544#39298544
Euri Pinhollow

Chỉ là một FYI, trong đoạn mã thứ hai của bạn, lambda của bạn quá dài dòng, hãy xem xét sự thay đổi này:std::function<int(int,int)> sum = [&](int a, int b) {
armanali

Câu trả lời:


189

Hãy suy nghĩ về sự khác biệt giữa phiên bản tự động và phiên bản loại được chỉ định đầy đủ. Các auto infers từ khóa loại của nó từ bất cứ nó khởi tạo với, nhưng những gì bạn đang khởi tạo nó với nhu cầu để biết những gì loại của nó là (trong trường hợp này, các nhu cầu lambda đóng cửa để biết các loại nó chụp). Một cái gì đó của một vấn đề gà và trứng.

Mặt khác, loại đối tượng chức năng được chỉ định đầy đủ không cần phải "biết" bất cứ điều gì về những gì được gán cho nó, và do đó, việc đóng của lambda có thể được thông báo đầy đủ về các kiểu mà nó bắt được.

Xem xét sửa đổi nhỏ mã này của bạn và nó có thể có ý nghĩa hơn:

std::function<int(int,int)> sum;
sum = [term,next,&sum](int a, int b)->int {
if(a>b)
    return 0;
else
    return term(a) + sum(next(a),b);
};

Rõ ràng, điều này sẽ không hoạt động với tự động . Các hàm lambda đệ quy hoạt động hoàn toàn tốt (ít nhất là chúng hoạt động trong MSVC, nơi tôi có kinh nghiệm với chúng), chỉ là chúng không thực sự tương thích với suy luận kiểu.


3
Tôi không đồng ý với điều này. Loại lambda nổi tiếng ngay khi cơ thể chức năng được nhập vào - không có lý do gì mà nó không nên được suy luận trước đó.
Cún con

16
@DeadMG nhưng thông số kỹ thuật cấm tham chiếu đến autobiến trong trình khởi tạo của nó. loại biến tự động chưa được biết khi trình khởi tạo đang được xử lý.
Julian Schaub - litb

1
Tự hỏi tại sao điều này không được đánh dấu là 'câu trả lời' và người Python đó được phân loại là 'Trả lời'?!
Ajay

1
@Puppy: Tuy nhiên, trong trường hợp chụp ngầm, để đạt hiệu quả, chỉ có các biến được tham chiếu thực sự bị bắt, do đó phần thân phải được phân tích cú pháp.
kec

Có một cách giải thích hợp lệ nào sumkhác không std::function<int(int, int)>, hoặc có thông số C ++ không bận tâm để suy ra nó?
Mateen Ulhaq

78

Bí quyết là để cung cấp cho bản thân lambda như là một tham số , chứ không phải bằng cách nắm bắt.

const auto sum = [term,next](int a, int b) {
  auto sum_impl=[term,next](int a,int b,auto& sum_ref) mutable {
    if(a>b){
      return 0;
    }
    return term(a) + sum_ref(next(a),b,sum_ref);
  };
  return sum_impl(a,b,sum_impl);
};

Tất cả các vấn đề trong khoa học máy tính có thể được giải quyết bằng một mức độ gián tiếp khác . Lần đầu tiên tôi tìm thấy thủ thuật dễ dàng này tại http://pedromelendez.com/blog/2015/07/16/recursive-lambdas-in-c14/

không yêu cầu C ++ 14 trong khi câu hỏi là về C ++ 11, nhưng có lẽ thú vị nhất với hầu hết.

Đi qua std::functioncũng có thể nhưng có thể dẫn đến mã chậm hơn. Nhưng không phải lúc nào cũng vậy. Hãy xem câu trả lời cho std :: function vs template


Đây không chỉ là một đặc thù của C ++, nó trực tiếp ánh xạ tới toán học của phép tính lambda. Từ Wikipedia :

Lambda calculus cannot express this as directly as some other notations:
all functions are anonymous in lambda calculus, so we can't refer to a
value which is yet to be defined, inside the lambda term defining that
same value. However, recursion can still be achieved by arranging for a
lambda expression to receive itself as its argument value

3
Điều này có vẻ tồi tệ hơn nhiều so với sử dụng rõ ràng function<>. Tôi không thể thấy tại sao mọi người thích nó. Chỉnh sửa: Nó nhanh hơn rõ ràng.
Timmmm

17
cách này tốt hơn std :: function vì 3 lý do: nó không yêu cầu xóa kiểu hoặc cấp phát bộ nhớ, nó có thể là constexpr và nó hoạt động đúng với các tham số / kiểu trả về tự động (templated)
Ivan Sanz-Carasa

3
Có lẽ giải pháp này cũng có lợi thế là có thể sao chép mà không cần tham chiếu hàm std :: ngoài phạm vi?
Uri Granta

3
Hừm, khi thử, GCC 8.1 (linux) đã phàn nàn: error: use of ‘[...]’ before deduction of ‘auto’- cần xác định rõ ràng loại trả về (mặt khác, không cần phải thay đổi).
Aconcagua

@Aconcagua tương tự ở đây với Xcode10 và tôi đã đặt tiêu chuẩn C ++ thành 17 chẵn
IceFire

39

Với C ++ 14, giờ đây khá dễ dàng để tạo một lambda đệ quy hiệu quả mà không phải chịu thêm chi phí std::function, chỉ trong một vài dòng mã (với một chỉnh sửa nhỏ từ bản gốc để ngăn người dùng lấy bản sao ngẫu nhiên ):

template <class F>
struct y_combinator {
    F f; // the lambda will be stored here

    // a forwarding operator():
    template <class... Args>
    decltype(auto) operator()(Args&&... args) const {
        // we pass ourselves to f, then the arguments.
        // [edit: Barry] pass in std::ref(*this) instead of *this
        return f(std::ref(*this), std::forward<Args>(args)...);
    }
};

// helper function that deduces the type of the lambda:
template <class F>
y_combinator<std::decay_t<F>> make_y_combinator(F&& f) {
    return {std::forward<F>(f)};
}

với sumnỗ lực ban đầu của bạn trở thành:

auto sum = make_y_combinator([term,next](auto sum, int a, int b) {
  if (a>b) {
    return 0;
  }
  else {
    return term(a) + sum(next(a),b);
  }
});

Trong C ++ 17, với CTAD, chúng ta có thể thêm một hướng dẫn khấu trừ:

template <class F> y_combinator(F) -> y_combinator<F>;

Điều này làm giảm nhu cầu về chức năng của người trợ giúp. Chúng tôi chỉ có thể viết y_combinator{[](auto self, ...){...}}trực tiếp.


Trong C ++ 20, với CTAD cho tổng hợp, hướng dẫn khấu trừ sẽ không cần thiết.


Điều này là tuyệt vời, nhưng người ta có thể xem xét std::forward<decltype(sum)>(sum)thay vì sumtrên dòng cuối cùng.
Johan Lundberg

@Johan Không, chỉ có một operator()vì vậy không có gì để đạt được bằng cách chuyển tiếpsum
Barry

Ồ, đó là sự thật. Không được sử dụng để sử dụng tài liệu tham khảo chuyển tiếp mà không chuyển tiếp.
Johan Lundberg

Y-combinator chắc chắn là con đường để đi. Nhưng bạn thực sự nên thêm một trường không constquá tải trong trường hợp đối tượng hàm được cung cấp có consttoán tử không gọi. Và sử dụng SFINAE và tính toán noexceptcho cả hai. Ngoài ra, không cần chức năng tạo trong C ++ 17 nữa.
Ded repeatator

2
@minex Có, auto sumbản sao ... nhưng nó sao chép a reference_wrapper, đó là điều tương tự như tham khảo. Làm điều đó một lần trong quá trình thực hiện có nghĩa là không có cách sử dụng nào sẽ vô tình sao chép.
Barry

22

Tôi có một giải pháp khác, nhưng chỉ hoạt động với lambdas không quốc tịch:

void f()
{
    static int (*self)(int) = [](int i)->int { return i>0 ? self(i-1)*i : 1; };
    std::cout<<self(10);
}

Thủ thuật ở đây là lambdas có thể truy cập các biến tĩnh và bạn có thể chuyển đổi các biến không trạng thái thành con trỏ hàm.

Bạn có thể sử dụng nó với lambdas tiêu chuẩn:

void g()
{
    int sum;
    auto rec = [&sum](int i) -> int
    {
        static int (*inner)(int&, int) = [](int& _sum, int i)->int 
        {
            _sum += i;
            return i>0 ? inner(_sum, i-1)*i : 1; 
        };
        return inner(sum, i);
    };
}

Nó hoạt động trong GCC 4.7


3
Điều này sẽ có hiệu suất tốt hơn chức năng std ::, vì vậy +1 cho giải pháp thay thế. Nhưng thực sự, tại thời điểm này tôi tự hỏi liệu sử dụng lambdas có phải là lựa chọn tốt nhất không?)
Antoine

Nếu bạn có lambda không trạng thái, bạn cũng có thể làm cho nó đầy đủ chức năng.
Timmmm

1
@Timmmm Nhưng sau đó, bạn rò rỉ một phần của việc thực hiện sang từ bên ngoài, thông thường lambdas được kết hợp chặt chẽ với chức năng cha mẹ (ngay cả khi không có ảnh chụp). Nếu đây không phải là trường hợp thì bạn không nên sử dụng lambdas ở nơi đầu tiên và sử dụng các chức năng bình thường của functor.
Yankes

10

Bạn có thể thực hiện một hàm lambda tự gọi đệ quy. Điều duy nhất bạn cần làm là tham chiếu nó thông qua trình bao bọc hàm để trình biên dịch biết kiểu trả về và kiểu đối số (bạn không thể nắm bắt một biến - chính lambda - chưa được xác định) .

  function<int (int)> f;

  f = [&f](int x) {
    if (x == 0) return 0;
    return x + f(x-1);
  };

  printf("%d\n", f(10));

Hãy cẩn thận để không chạy ra khỏi phạm vi của trình bao bọc f.


3
Nhưng, điều này giống hệt với câu trả lời được chấp nhận và có thể bị phạt vì sử dụng chức năng tiêu chuẩn.
Johan Lundberg

9

Để làm đệ quy lambda mà không cần sử dụng các lớp và hàm bên ngoài (như std::functionhoặc tổ hợp điểm cố định), người ta có thể sử dụng cấu trúc sau trong C ++ 14 ( ví dụ trực tiếp ):

#include <utility>
#include <list>
#include <memory>
#include <iostream>

int main()
{
    struct tree
    {
        int payload;
        std::list< tree > children = {}; // std::list of incomplete type is allowed
    };
    std::size_t indent = 0;
    // indication of result type here is essential
    const auto print = [&] (const auto & self, const tree & node) -> void
    {
        std::cout << std::string(indent, ' ') << node.payload << '\n';
        ++indent;
        for (const tree & t : node.children) {
            self(self, t);
        }
        --indent;
    };
    print(print, {1, {{2, {{8}}}, {3, {{5, {{7}}}, {6}}}, {4}}});
}

in:

1
 2
  8
 3
  5
   7
  6
 4

Lưu ý, loại kết quả của lambda nên được chỉ định rõ ràng.


6

Tôi đã chạy một điểm chuẩn so sánh một hàm đệ quy với một hàm lambda đệ quy bằng std::function<>phương thức chụp. Với tối ưu hóa đầy đủ được kích hoạt trên phiên bản clang 4.1, phiên bản lambda chạy chậm hơn đáng kể.

#include <iostream>
#include <functional>
#include <chrono>

uint64_t sum1(int n) {
  return (n <= 1) ? 1 : n + sum1(n - 1);
}

std::function<uint64_t(int)> sum2 = [&] (int n) {
  return (n <= 1) ? 1 : n + sum2(n - 1);
};

auto const ITERATIONS = 10000;
auto const DEPTH = 100000;

template <class Func, class Input>
void benchmark(Func&& func, Input&& input) {
  auto t1 = std::chrono::high_resolution_clock::now();
  for (auto i = 0; i != ITERATIONS; ++i) {
    func(input);
  }
  auto t2 = std::chrono::high_resolution_clock::now();
  auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2-t1).count();
  std::cout << "Duration: " << duration << std::endl;
}

int main() {
  benchmark(sum1, DEPTH);
  benchmark(sum2, DEPTH);
}

Tạo ra kết quả:

Duration: 0 // regular function
Duration: 4027 // lambda function

(Lưu ý: Tôi cũng đã xác nhận với một phiên bản lấy đầu vào từ cin, để loại bỏ đánh giá thời gian biên dịch)

Clang cũng tạo ra một cảnh báo trình biên dịch:

main.cc:10:29: warning: variable 'sum2' is uninitialized when used within its own initialization [-Wuninitialized]

Đó là dự kiến, và an toàn, nhưng cần lưu ý.

Thật tuyệt khi có một giải pháp trong các công cụ của chúng tôi, nhưng tôi nghĩ ngôn ngữ sẽ cần một cách tốt hơn để xử lý trường hợp này nếu hiệu suất tương đương với các phương pháp hiện tại.

Ghi chú:

Như một nhà bình luận đã chỉ ra, có vẻ như phiên bản mới nhất của VC ++ đã tìm ra cách tối ưu hóa điều này đến điểm hiệu năng tương đương. Rốt cuộc, có lẽ chúng ta không cần một cách tốt hơn để xử lý việc này (ngoại trừ đường cú pháp).

Ngoài ra, như một số bài viết SO khác đã nêu trong những tuần gần đây, hiệu suất của std::function<>chính nó có thể là nguyên nhân gây ra sự chậm chạp so với chức năng gọi điện, ít nhất là khi chụp lambda quá lớn để phù hợp với một số std::functionsử dụng không gian được tối ưu hóa cho thư viện cho các chức năng nhỏ (Tôi đoán giống như các tối ưu hóa chuỗi ngắn khác nhau?).


2
-1. Lưu ý rằng lý do duy nhất khiến phiên bản "lambda" mất nhiều thời gian hơn là do bạn liên kết nó với hàm std ::, khiến cho toán tử () gọi một cuộc gọi ảo và điều đó rõ ràng sẽ mất nhiều thời gian hơn. Trên hết, mã của bạn, trong chế độ phát hành VS2012, mất khoảng thời gian như nhau trong cả hai trường hợp.
Yam Marcovic

@YamMarcovic Gì? Đó hiện là cách duy nhất được biết để viết lambda đệ quy (đó là điểm của ví dụ). Tôi rất vui khi biết rằng VS2012 đã tìm ra cách tối ưu hóa trường hợp sử dụng này (mặc dù gần đây đã có nhiều phát triển hơn về chủ đề này, rõ ràng nếu lambda của tôi đã nắm bắt được nhiều hơn thì nó sẽ không phù hợp với chức năng std :: small- tối ưu hóa bộ nhớ functor hoặc whatnot).
mmocny

2
Công nhận. Tôi hiểu nhầm bài của bạn. +1 rồi. Gah, chỉ có thể upvote nếu bạn chỉnh sửa câu trả lời này. Vì vậy, bạn có thể nhấn mạnh nó nhiều hơn một chút, chẳng hạn như trong bình luận?
Yam Marcovic

1
@YamMarcovic Xong. Tôi đánh giá cao sự sẵn lòng của bạn để cung cấp thông tin phản hồi và tinh chỉnh nó khi cần thiết. +1 cho bạn, thưa ông.
mmocny

0 thời gian thường có nghĩa là "toàn bộ hoạt động đã được tối ưu hóa đi". Lấy đầu vào từ cin không làm gì nếu trình biên dịch chứng minh rằng bạn không làm gì với tính toán lại của tính toán.
Yakk - Adam Nevraumont

1

Đây là một triển khai đơn giản hơn một chút của toán tử fixpoint, điều này làm cho nó rõ ràng hơn một chút chính xác những gì đang diễn ra.

#include <iostream>
#include <functional>

using namespace std;

template<typename T, typename... Args>
struct fixpoint
{
    typedef function<T(Args...)> effective_type;
    typedef function<T(const effective_type&, Args...)> function_type;

    function_type f_nonr;

    T operator()(Args... args) const
    {
        return f_nonr(*this, args...);
    }

    fixpoint(const function_type& p_f)
        : f_nonr(p_f)
    {
    }
};


int main()
{
    auto fib_nonr = [](const function<int(int)>& f, int n) -> int
    {
        return n < 2 ? n : f(n-1) + f(n-2);
    };

    auto fib = fixpoint<int,int>(fib_nonr);

    for (int i = 0; i < 6; ++i)
    {
        cout << fib(i) << '\n';
    }
}

Tôi nghĩ rằng bạn có thể cải thiện câu trả lời của mình (hiệu suất khôn ngoan) nếu bạn thay thế std::functionbằng con trỏ hàm (của lõi, nó sẽ chỉ hoạt động với chức năng bình thường và lambdas không trạng thái). Btw fib_nonrnên chấp nhận fixpoint<int,int>, nếu bạn sử dụng std::functionyêu cầu sao chép mới *this.
Yankes

1

Đây là phiên bản tinh chỉnh của giải pháp Y-combinator dựa trên một đề xuất của @Barry.

template <class F>
struct recursive {
  F f;
  template <class... Ts>
  decltype(auto) operator()(Ts&&... ts)  const { return f(std::ref(*this), std::forward<Ts>(ts)...); }

  template <class... Ts>
  decltype(auto) operator()(Ts&&... ts)  { return f(std::ref(*this), std::forward<Ts>(ts)...); }
};

template <class F> recursive(F) -> recursive<F>;
auto const rec = [](auto f){ return recursive{std::move(f)}; };

Để sử dụng điều này, người ta có thể làm như sau

auto fib = rec([&](auto&& fib, int i) {
// implementation detail omitted.
});

Nó tương tự như let rectừ khóa trong OCaml, mặc dù không giống nhau.


0

C ++ 14: Đây là một bộ lambdas ẩn danh ẩn danh đệ quy / không bắt giữ, xuất ra tất cả các số từ 1, 20

([](auto f, auto n, auto m) {
    f(f, n, m);
})(
    [](auto f, auto n, auto m) -> void
{
    cout << typeid(n).name() << el;
    cout << n << el;
    if (n<m)
        f(f, ++n, m);
},
    1, 20);

Nếu tôi hiểu chính xác thì đây là sử dụng giải pháp Y-combinator

Và đây là phiên bản tổng (n, m)

auto sum = [](auto n, auto m) {
    return ([](auto f, auto n, auto m) {
        int res = f(f, n, m);
        return res;
    })(
        [](auto f, auto n, auto m) -> int
        {
            if (n > m)
                return 0;
            else {
                int sum = n + f(f, n + 1, m);
                return sum;
            }
        },
        n, m); };

auto result = sum(1, 10); //result == 55

-1

Đây là câu trả lời cuối cùng cho OP. Dù sao, Visual Studio 2010 không hỗ trợ chụp các biến toàn cục. Và bạn không cần phải nắm bắt chúng bởi vì biến toàn cục có thể truy cập trên toàn cầu theo định nghĩa. Câu trả lời sau sử dụng biến cục bộ thay thế.

#include <functional>
#include <iostream>

template<typename T>
struct t2t
{
    typedef T t;
};

template<typename R, typename V1, typename V2>
struct fixpoint
{
    typedef std::function<R (V1, V2)> func_t;
    typedef std::function<func_t (func_t)> tfunc_t;
    typedef std::function<func_t (tfunc_t)> yfunc_t;

    class loopfunc_t {
    public:
        func_t operator()(loopfunc_t v)const {
            return func(v);
        }
        template<typename L>
        loopfunc_t(const L &l):func(l){}
        typedef V1 Parameter1_t;
        typedef V2 Parameter2_t;
    private:
        std::function<func_t (loopfunc_t)> func;
    };
    static yfunc_t fix;
};
template<typename R, typename V1, typename V2>
typename fixpoint<R, V1, V2>::yfunc_t fixpoint<R, V1, V2>::fix = [](tfunc_t f) -> func_t {
    return [f](fixpoint<R, V1, V2>::loopfunc_t x){  return f(x(x)); }
    ([f](fixpoint<R, V1, V2>::loopfunc_t x) -> fixpoint<R, V1, V2>::func_t{
        auto &ff = f;
        return [ff, x](t2t<decltype(x)>::t::Parameter1_t v1, 
            t2t<decltype(x)>::t::Parameter1_t v2){
            return ff(x(x))(v1, v2);
        }; 
    });
};

int _tmain(int argc, _TCHAR* argv[])
{
    auto term = [](int a)->int {
      return a*a;
    };

    auto next = [](int a)->int {
      return ++a;
    };

    auto sum = fixpoint<int, int, int>::fix(
    [term,next](std::function<int (int, int)> sum1) -> std::function<int (int, int)>{
        auto &term1 = term;
        auto &next1 = next;
        return [term1, next1, sum1](int a, int b)mutable ->int {
            if(a>b)
                return 0;
        else
            return term1(a) + sum1(next1(a),b);
        };
    });

    std::cout<<sum(1,10)<<std::endl; //385

    return 0;
}

Có thể làm cho trình biên dịch câu trả lời này bất khả tri?
rayryeng

-2

Bạn đang cố gắng nắm bắt một biến (tổng) bạn đang ở giữa định nghĩa. Điều đó không thể tốt được.

Tôi không nghĩ thực sự tự đệ quy C ++ 0x lambdas là có thể. Bạn sẽ có thể chụp các lambdas khác, mặc dù.


3
nhưng nó hoạt động nếu khai báo tổng được thay đổi từ 'auto' thành std :: function <int (int, int)> mà không thay đổi danh sách chụp.
weima

Bởi vì nó không còn là lambda nữa, mà là một chức năng có thể được sử dụng thay cho lambda?
Hamish Grubijan

-2

Câu trả lời này kém hơn câu trả lời của Yankes, nhưng vẫn vậy, đây là:

using dp_type = void (*)();

using fp_type = void (*)(dp_type, unsigned, unsigned);

fp_type fp = [](dp_type dp, unsigned const a, unsigned const b) {
  ::std::cout << a << ::std::endl;
  return reinterpret_cast<fp_type>(dp)(dp, b, a + b);
};

fp(reinterpret_cast<dp_type>(fp), 0, 1);

Tôi nghĩ bạn nên tránh reinterpret_cast. Có lẽ cách tốt nhất trong trường hợp của bạn là tạo một số cấu trúc thay thế dp_type. Nó nên có trường fp_type, có thể được xây dựng từ fp_typevà có toán tử ()với các đối số như fp_type. Điều này sẽ gần std::functionnhưng sẽ cho phép đối số tự tham chiếu.
Yankes

Tôi muốn đăng một ví dụ tối thiểu, không có cấu trúc, thoải mái chỉnh sửa câu trả lời của tôi và cung cấp một giải pháp hoàn chỉnh hơn. A structcũng sẽ thêm một mức độ bổ sung. Ví dụ hoạt động và dàn diễn viên tuân thủ tiêu chuẩn, tôi không biết những gì -1đã được thực hiện.
dùng1095108

không, struct sẽ chỉ hoạt động như bộ chứa cho con trỏ và sẽ được truyền dưới dạng giá trị. Điều này sẽ không có nhiều chỉ dẫn hoặc chi phí hơn con trỏ. Và về -1tôi không biết ai đưa nó cho bạn, nhưng tôi nghĩ rằng nó reinterpret_castnên được sử dụng như là phương sách cuối cùng.
Yankes

Các castđược cho là đảm bảo để làm việc theo tiêu chuẩn c ++ 11. Sử dụng một struct, trong mắt tôi, có thể đánh bại việc sử dụng một đối tượng lambda. Rốt cuộc, người structbạn đề xuất là một functor, sử dụng một đối tượng lambda.
dùng1095108

Nhìn vào giải pháp @Pseudonymous, chỉ xóa std::functionvà bạn sẽ có một cái gì đó gần với cái mà tôi có trong tâm trí. Điều này có thể sẽ có hiệu suất tương tự như giải pháp của bạn.
Yankes

-3

Bạn cần một tổ hợp điểm cố định. Xem này .

hoặc xem mã sau đây:

//As decltype(variable)::member_name is invalid currently, 
//the following template is a workaround.
//Usage: t2t<decltype(variable)>::t::member_name
template<typename T>
struct t2t
{
    typedef T t;
};

template<typename R, typename V>
struct fixpoint
{
    typedef std::function<R (V)> func_t;
    typedef std::function<func_t (func_t)> tfunc_t;
    typedef std::function<func_t (tfunc_t)> yfunc_t;

    class loopfunc_t {
    public:
        func_t operator()(loopfunc_t v)const {
            return func(v);
        }
        template<typename L>
        loopfunc_t(const L &l):func(l){}
        typedef V Parameter_t;
    private:
        std::function<func_t (loopfunc_t)> func;
    };
    static yfunc_t fix;
};
template<typename R, typename V>
typename fixpoint<R, V>::yfunc_t fixpoint<R, V>::fix = 
[](fixpoint<R, V>::tfunc_t f) -> fixpoint<R, V>::func_t {
    fixpoint<R, V>::loopfunc_t l = [f](fixpoint<R, V>::loopfunc_t x) ->
        fixpoint<R, V>::func_t{
            //f cannot be captured since it is not a local variable
            //of this scope. We need a new reference to it.
            auto &ff = f;
            //We need struct t2t because template parameter
            //V is not accessable in this level.
            return [ff, x](t2t<decltype(x)>::t::Parameter_t v){
                return ff(x(x))(v); 
            };
        }; 
        return l(l);
    };

int _tmain(int argc, _TCHAR* argv[])
{
    int v = 0;
    std::function<int (int)> fac = 
    fixpoint<int, int>::fix([](std::function<int (int)> f)
        -> std::function<int (int)>{
        return [f](int i) -> int{
            if(i==0) return 1;
            else return i * f(i-1);
        };
    });

    int i = fac(10);
    std::cout << i; //3628800
    return 0;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.