Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/133.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用对lambda的引用作为映射中的比较器(正确的方法?)_C++_C++11_Lambda - Fatal编程技术网

C++ 使用对lambda的引用作为映射中的比较器(正确的方法?)

C++ 使用对lambda的引用作为映射中的比较器(正确的方法?),c++,c++11,lambda,C++,C++11,Lambda,我想在std::map中使用lambda作为自定义比较器,但不幸的是,Visual Studio 2013编译器不允许使用这样的简单代码: auto cmp = [](int l, int r) { return l < r; }; std::map<int, int, decltype(cmp)> myMap(cmp); myMap[1] = 1; 所以我的最后一个问题是:通过任何方式保证将原始引用类型作为比较器传递是安全的吗?或者它取决于STL实现者,在某些情况下可能会中

我想在
std::map
中使用lambda作为自定义比较器,但不幸的是,Visual Studio 2013编译器不允许使用这样的简单代码:

auto cmp = [](int l, int r) { return l < r; };
std::map<int, int, decltype(cmp)> myMap(cmp);
myMap[1] = 1;
所以我的最后一个问题是:通过任何方式保证将原始引用类型作为比较器传递是安全的吗?或者它取决于STL实现者,在某些情况下可能会中断,因此,使用
reference\u wrapper
是一种方法吗

最后一点注意:我认为在VS2013世界之外传递引用(以任何形式)可能很有用,以防出于某种原因不希望复制比较器

干杯, 罗斯蒂斯拉夫

编辑: 还有一个区别:

int compCounter = 0;
auto cmp = [&compCounter](int l, int r) mutable { ++compCounter; return l < r; };

//using cmpT = decltype(cmp)&;
using cmpT = std::reference_wrapper<decltype(cmp)>;

std::map<int, int, cmpT> myMap(cmp); 
myMap[1] = 1;

// Will work in both cases of cmpT
std::map<int, int, cmpT> m2(myMap);

// Will work only for reference_wrapper
std::map<int, int, cmpT> m2(cmp);
m2 = myMap;
int compCounter=0;
自动cmp=[&compCounter](int l,int r)可变{++compCounter;返回l
首先,请注意lambda表达式是一个临时对象,而不是
const
对象。可以很好地将其绑定到右值引用:

int compCounter = 0;
auto&& tmp = [compCounter](int l, int r) mutable { ++compCounter; return l < r; };
auto&  cmp = tmp;
std::map<int, int, decltype(cmp)> myMap(cmp);
myMap[1] = 1;
int compCounter=0;
auto&&tmp=[compCounter](intl,intr)可变{++compCounter;返回l
这段代码首先将lambda对象绑定到右值引用。由于右值引用是左值,因此可以将名称绑定到左值引用。然后,左值引用可以与
std::map
一起使用

除了能够比较键类型之外,我能在比较对象上找到的唯一要求是它是
可复制的
(在表102“关联容器要求”中)。基于
std::is\u copy\u constructible::value
它是
CopyConstructible


这段代码当然是用gcc和clang编译的。我没有可访问的MSVC++来检查它是否也使用MSVC++编译。

错误消息
无法构造lambda的实例
实际上是一个实例。据说这只发生在不捕获任何内容的lambda上,因此建议的解决方法是捕获一个虚拟变量。(实际上我没有测试过,我找不到足够的在线编译器来进行VisualC++)

使用对lambda的引用而不是lambda本身也可以避免这种优化,所以
const auto&cmp=…
也可以工作。可变lambda的常量引用失败,因为
decltype(cmp)
将常量限定符带到映射中,而不是
map(cmp)
接收常量引用然后创建非常量副本。中的代码创建了一个非常量引用,因此可以正常工作

使用引用作为模板参数 我在这里不是一个真正的专家,但无论如何我会尝试

正如迪特玛在他的回答中所说的,比较器必须是可复制的。显而易见的解释是,容器构造函数将其作为常量引用接收,然后创建一个内部副本

当您使用
CompareClass&
作为模板参数时,
CompareClass
本身是否可复制并不重要,因为引用总是可复制的。但是,在这种情况下,映射将保存引用的副本,而不是对象本身的副本

明显的结果是,您必须确保引用的对象不会过早释放。此外,所有副本都将引用同一对象,而不是每个副本都有自己的副本。除此之外,不应该发生什么坏事

因此,如果您可以跟踪引用,并确保它们都会在对象本身之前消亡,我认为这是安全的。另一方面,它可能不够透明,有人可能会误解您的代码并破坏它。另外,请注意,在
auto&a=…
之后,
decltype(a)
也将是引用类型,这一点更加模糊

关于有状态映射比较器的注记
在VisualStudio中,
map
从const限定方法内部调用比较器。这意味着比较器的
运算符()。也就是说,有状态比较器必须“假装”为无状态,例如,将状态存储在可变字段或通过引用存储的其他对象中。将比较器存储为引用类型也是可行的。

这很奇怪,我认为这是完全合法的代码。。。不过,您可以选择使用
std::function
。出于好奇,VS2013是如何构成的:const std::function f=[](int l,int r){return lstd::map
的实现中构造lambda。我认为这是根据论点构建的副本。@AaryamanSagar是的,我认为这是可能的。但我怀疑效率是否会相当高。除了在lambda状态较大且不允许小函数优化的情况下所需的分配之外,它始终是一个额外的间接寻址,因为类型擦除和编译器内联它们时会遇到问题(有关详细和简短的解释,请参阅),所以性能可能会有很大差异。@patros,我相信这很简单,因为它复制了lambda对象,然后删除了它的类型(同样,分配可能会根据lambda的大小而发生,在无状态lambda的情况下,如您的示例中所示-可能不会,因为SFO会起作用)。好的观点,Dietmar。但是,我非常确定这段代码相当于创建lambda对象然后使用
decltype(cmp)&
的代码片段,它在堆栈上创建了一个对象(通过绑定右值引用延长其生存期的临时对象,或仅是一个命名对象)然后使用
编译器\u lambda\u name&
作为模板参数。探索
int compCounter = 0;
auto cmp = [&compCounter](int l, int r) mutable { ++compCounter; return l < r; };
std::map<int, int, decltype(cmp)&> myMap(cmp);
myMap[1] = 1;
int compCounter = 0;
auto cmp = [&compCounter](int l, int r) mutable { ++compCounter; return l < r; };
std::map<int, int, std::reference_wrapper<decltype(cmp)>> myMap(cmp);
myMap[1] = 1;
int compCounter = 0;
auto cmp = [&compCounter](int l, int r) mutable { ++compCounter; return l < r; };

//using cmpT = decltype(cmp)&;
using cmpT = std::reference_wrapper<decltype(cmp)>;

std::map<int, int, cmpT> myMap(cmp); 
myMap[1] = 1;

// Will work in both cases of cmpT
std::map<int, int, cmpT> m2(myMap);

// Will work only for reference_wrapper
std::map<int, int, cmpT> m2(cmp);
m2 = myMap;
int compCounter = 0;
auto&& tmp = [compCounter](int l, int r) mutable { ++compCounter; return l < r; };
auto&  cmp = tmp;
std::map<int, int, decltype(cmp)> myMap(cmp);
myMap[1] = 1;