能够在lambda中使用未捕获的局部变量的编译时特征有什么意义?



我注意到一个人被允许使用未在lambda中捕获的变量的编译时特征,例如调用sizeof, decltype函数,例如:

#include <iostream>
void f ()
{
}
int main()
{
  int y = 13;
  auto x = []{ return sizeof (decltype (y));};
  std::cout << x () << 'n';
}

由于g++clang++编译这个程序都没有错误,我想这是标准允许的。

这似乎有点误导我,即使我想不出任何特定的恶意情况下,它会导致一个错误。但是我想知道这个特性的实际用例是什么?

一个简单的例子,你可能会使用这是,如果你有一个lambda,你想执行计算相同类型的y,因为你会将结果分配给y

同样,从另一个角度考虑:在[=]{ return x + sizeof (y);}中捕获y的好处是什么?这样做绝对没有意义,因为y实际上并没有在lambda内部使用。捕获y只会增加完全没有意义的开销。不仅如此,它甚至可能使编译器的内部工作复杂化,因为它们将不再能够简单地优化y, sizeof(y)不再在语义上等同于sizeof(int)

有时在嵌套作用域(lambda)中需要不带值的类型信息。你总是可以直接命名类型(或模板参数),如果你可以访问它,但总是有一个很好的建议,即如果有一天你改变了变量的类型,你不必在所有其他重复它的表达式中改变它。

例如:

#include <iostream>
#include <tuple>
#include <utility>
class   storage
{
public:
  template<typename T>
  auto    make_getter(T value)
  {
    std::get<decltype(value)>(storage_) = value;
    auto getter = [this]
      {
        return std::get<decltype(value)>(storage_);
      };
    return getter;
  }
private:
  std::tuple<int, char, double>   storage_;
};
int     main(void)
{
  storage       s;
  auto getter = s.make_getter(42);
  std::cout << getter() << std::endl;
}

这里你总是可以使用std::get<T>而不是std::get<decltype(value)>,但是如果有一天make_getter不再是模板,它成为一个普通的函数,为每个类型的元组重载,而值的类型将更改为int,这里的优点是decltype(value)将始终工作,只要你不重命名变量。

无论如何,我认为这个功能的实用级别可能更多的是语义而不是技术。这种行为可能是从传统的规范

继承来的。
char *buffer = malloc(42 * sizeof(*buffer));
C语言中使用

代替

char *buffer = malloc(42 *sizeof(char));

同样的原因。

同样,如果类型名是难以忍受的,并且由于某种原因你不想别名,你可以使用decltype方式,这并不一定意味着你想要关联的值

最新更新