Tob*_*ann 17 c++ lambda decltype language-lawyer
以下代码适用于gcc
#include <map>
int main() {
std::map<int, double> dict;
const auto lambda = [&]()
{
decltype(dict)::value_type bar;
};
}
Run Code Online (Sandbox Code Playgroud)
但对于msvc我还要另外使用std::remove_reference
#include <map>
#include <type_traits>
int main() {
std::map<int, double> dict;
const auto lambda = [&]()
{
std::remove_reference_t<decltype(dict)>::value_type bar;
};
}
Run Code Online (Sandbox Code Playgroud)
否则我收到一个错误:
error C2651: 'std::map<int,double,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>> &': left of '::' must be a class, struct or union
Run Code Online (Sandbox Code Playgroud)
哪个编译器根据标准显示正确的行为?
更新:
对于msvc decltype(dict)确实是一个参考,如下面的代码
#include <map>
int main()
{
std::map<int, double> dict;
const auto lambda = [&]()
{
decltype(dict) foo;
};
}
Run Code Online (Sandbox Code Playgroud)
错误
error C2530: 'foo': references must be initialized
Run Code Online (Sandbox Code Playgroud)
如果这确实是错误的行为,它可能会导致令人讨厌的错误,比如使用msvc编译代码时的悬空引用.
#include <map>
std::map<int, double> return_a_map()
{
std::map<int, double> result;
return result;
}
int main()
{
std::map<int, double> dict;
const auto lambda = [&]()
{
decltype(dict) foo = return_a_map();
// foo is a dangling reference in msvc
};
}
Run Code Online (Sandbox Code Playgroud)
Col*_*mbo 12
关于非括号应用的特殊规则decltype(即[expr.prim.lambda]/20不适用).所以我们回到通常的定义decltype,它要求如果操作数是一个id-expression,则yielding类型只是实体的声明类型,而不是引用类型.因此VC++是错误的.
注意:是否dict被捕获并不重要,因为17:
lambda 表达式的复合语句中的每个id表达式(由副本捕获的实体的odr-use(3.2))将转换为对闭包类型的相应未命名数据成员的访问.[ 注意:不是odr-use 的id表达式是指原始实体,永远不是闭包类型的成员.此外,这样的id表达式不会导致实体的隐式捕获.- 结束说明 ]
decltype永远不要使用任何操作数或子操作.这条规则实际上有时会遇到很多问题,例如核心问题958所示:
Run Code Online (Sandbox Code Playgroud)int f (int&); void* f (const int&); int main() { int i; [=] ()-> decltype(f(i)) { return f(i); }; }
这里,decltype(f(i))使用非const i封闭范围.但是,由于lambda不是mutable,i实际上是正文const,因此尾随返回类型是不正确的.CWG总结说,这种情况很少发生,值得解决.