【问题标题】:How can I rewrite this javascript code to C++11?如何将此 javascript 代码重写为 C++11?
【发布时间】:2012-02-22 22:02:07
【问题描述】:

这是我在 Javascript Definitive Guide 中看到的 javascript 闭包代码。我想把它写成 C++11

var uniqueID1 = (function()
{
    var id = 0;
    return function() { return id++; };
})();    

这是我写的cpp代码。但它没有被编译。 C++11可以表示同一个表达式吗?

auto c = []() -> int (*)() { int x = 0; return [&x]() -> int { return x++; }};

我正在使用 VS2010

编辑: 这是我制作的完整的 javascript 示例代码。您可以轻松地测试代码在您的网络浏览器上的工作方式。

<head>
<script language="javascript">
var uniqueID1 = (function()
{
    var id = 0;
    return function() { return id++; };
})();

var uniqueID2 = (function()
{
      var id = 0;
      return function() { return id++; };
})();
</script>
</head>

<body>
        <input value = "uniqueid1" type="button" OnClick="alert(uniqueID1());"></input>
        <input value = "uniqueid2" type="button" OnClick="alert(uniqueID2());"></input>
</body>
</html>

【问题讨论】:

  • 哇,这是我见过的最不像 C++ 的 C++。我什至不知道您可以在任何 C 派生语言中按值返回新创建的函数。 编辑:你不能,正如 Nicol Bolas 提到的那样。

标签: javascript c++ lambda c++11 closures


【解决方案1】:

好的,首先让我们分解一下你的 JavaScript 做了什么。

function()
{
    var id = 0;
    return function() { return id++; };
}

这会创建一个函数。一个没有名字的函数,它没有参数。该函数在调用时将返回一个新函数。内部函数将可以访问局部范围的变量,并且该变量将成为该内部函数状态的一部分。

每次调用这个函数时,都会返回一个new函数。这个新函数将有自己的状态(毕竟var id = 0; 必须做一些事情)。每次调用外部函数都会返回一个具有新状态的新函数。

这个:

(function()
{
    var id = 0;
    return function() { return id++; };
})

将函数包装在一对括号中。这是运算符优先规则所必需的,以使其正常工作:

(function()
{
    var id = 0;
    return function() { return id++; };
})()

这会创建一个函数,然后调用该函数。这就是最后一个() 在最后所做的。它调用外部函数,外部函数创建一个具有自己作用域的内部函数并返回它。

在这个表达式中,外部函数丢失了。它消失了;你不能再访问它了。您创建了足够长的时间来调用它,然后将其放入垃圾收集的垃圾桶中。

鉴于所有这些,我们可以看到以下语句:

var uniqueID1 = (function()
{
    var id = 0;
    return function() { return id++; };
})();

创建外部函数,调用返回新函数的函数,然后将内部函数存储在名为uniqueID1的变量中。

如果您需要证明这是真的,请在您的 HTML 中尝试:

var uniqueMaker = function()
{
    var id = 0;
    return function() { return id++; };
};

var uniqueID1 = uniqueMaker();
var uniqueID2 = uniqueMaker();

您会得到与复制粘贴版本相同的答案。

如果我们希望 C++ 代码模仿这一点,我们需要使用正确的 C++ 代码执行每个步骤。

首先,内部函数。在 C++ 中,词法范围不存在。因此,您不能返回引用其他范围内变量的函数。您只能从另一个范围返回具有 copy 变量的 lambda。此外,除非您明确允许,否则 lambda 不能修改范围。因此,内部代码必须如下所示:

int id = 0;
return [=]() mutable { return ++id; };

这一切都很好,但是现在我们需要创建一个 lambda 来返回这个有状态的 lambda 函数。在 C++ 中,具有状态的函数与函数指针不同。而且由于 lambda 的类型是编译器定义的,因此无法输入其名称。因此,我们必须使用std::function 作为外部函数的返回类型。

[]() -> std::function<int()>
{
  int x = 0;
  return [=]() mutable { return x++; };
}

这将创建一个 lambda 函数,当调用该函数时,将返回一个具有自己状态的内部函数。独立于对该函数的任何后续调用的状态。就像 JavaScript 示例一样。

现在,这还不够。请记住,您的 JavaScript 创建外部函数,调用它,并存储它的返回值。外部函数本身被丢弃。所以我们需要模仿这个。幸运的是,这在 C++ 中很容易;它看起来就像 JavaScript:

([]() -> std::function<int()>
{
  int x = 0;
  return [=]() mutable { return x++; };
})()

最后,我们把它放在一个变量中:

auto uniqueID1 = ([]() -> std::function<int()>
{
  int x = 0;
  return [=]() mutable { return x++; };
})();

uniqueID1 的类型将是 std::function&lt;int()&gt;。它不会是外部函数的类型。外部函数只是用于创建内部作用域的临时函数。

【讨论】:

  • 仍未编译。 error C3491: 'x': a by-value capture cannot be modified in a non-mutable lambda我应该通过 gcc 测试它吗?
  • @Benjamin:然后让它可变,如编辑后的版本所示;)
  • @Benjamin well 0 0 0 是正确的响应,如果你想让它打印 0 1 2 那么你应该将 x 声明为静态 static int x =0; 或保存 c() 的结果,因为c() 实际上每次调用时都会设置x=0
  • @Benjamin:当然它会返回0 0 0c() 返回一个存储状态的函数。对c() 的每次调用都会返回一个具有自己的separate 状态的不同函数。除非我读错了您的 JavaScript 代码,否则您的 JavaScript 代码也是如此。请注意,您的 JavaScript 以() 结尾,它调用 外部函数。因此uniqueID1 存储了外部函数返回的函数。有关详细信息,请参阅我的编辑。
  • @Benjamin:我已经完全重写了我的答案,以解释 JavaScript 代码的工作原理以及如何在 C++ 中复制其效果。
【解决方案2】:

我假设您不是在要求字面翻译,而是如何在 C++ 中最好地表达这个结构。您的 JavaScript 构造在某种意义上是“伪造构造函数”,并且您的 ID 生成器最好用 C++ 中的简单 class 表示。然后您只需创建该类的实例:

class UniqueID
{
    unsigned int value;
public:
    UniqueID() : value(0) { }
    unsigned int operator()() { return ++value; }
};

用法:

UniqueID gen1, gen2;

some_function(gen1());
another_function(gen2());
Foo x(Blue, "Jim", gen1());

这种方法比 std::function 包装器更轻量级,尽管直接 lambda 会产生类似的数据结构(尽管您不知道它的类型名称)。如果您希望第一个 ID 为 0,则可以将 value 初始化为 -1(尽管保留 0 作为特殊值可能很有用)。

【讨论】:

  • 这不是一回事。如果我正确理解 JavaScript,则对 JavaScript 的每次调用都应返回具有 new 状态的函数的新实例。
  • @NicolBolas:好的,已编辑。多重 lambda 加 std::function 方法以一种有趣的方式很有趣,但它的成本比惯用的翻译要高得多。
  • 我不知道我是否会称它为“惯用语”。 C++11 将有自己的习语,而 Lambda 将成为其中的一部分。必须为这种事情创建一个类是过去的遗物。习语变了。
猜你喜欢
  • 2015-07-05
  • 2021-12-24
  • 2018-04-15
  • 2022-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-14
  • 1970-01-01
相关资源
最近更新 更多