能够在 lambda 中使用非捕获局部变量的编译时特性有什么意义?
What is the point of being able to use compile-time characteristics of non-captured local variables in a 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 方式,这并不一定意味着您需要相关值。
我注意到有人允许使用未在 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 方式,这并不一定意味着您需要相关值。