如何将这个javascript代码重写为C ++ 11?

时间:2012-01-31 06:15:14

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

这是我在Javascript权威指南中看到的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示例代码。您可以轻松测试代码在Web浏览器中的工作方式。

<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>

2 个答案:

答案 0 :(得分:19)

好的,首先让我们分解你的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。此外,除非您明确允许,否则lambdas无法修改范围。因此,内部代码必须如下所示:

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<int()>。它不是外部函数的类型。外部函数只是用于创建内部范围的临时函数。

答案 1 :(得分:0)

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

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保留为特殊值可能很有用)。