Lambda函数,奇怪的行为

时间:2015-03-06 07:29:39

标签: c++ c++11 lambda

假设我们在全局命名空间中声明了以下lambda:

auto Less = [](int a,int b) -> bool
{
    return a < b;
}

以下代码使用了这个lambda:

template<typename T>
struct foo
{
    foo(int v){}
    bool operator<(const foo<T>&) const
    {
        return T(1,2);
    }
};

int main()
{
    typedef foo<decltype(Less)> be_less;
    priority_queue<be_less> data;
}

如您所见,我使用Less作为结构foo的模板参数。使用g ++ 4.9.2,此代码无法编译:

test1.cpp:13:21: error: no matching function for call to '<lambda(int, int)>::__lambda0(int, int)'
     return T(1,2);
                 ^
test1.cpp:13:21: note: candidates are:
test1.cpp:17:14: note: constexpr<lambda(int, int)>::<lambda>(const<lambda(int, int)>&)
 auto Less = [](int a,int b) -> bool
          ^
test1.cpp:17:14: note:   candidate expects 1 argument, 2 provided
test1.cpp:17:14: note: constexpr<lambda(int, int)>::<lambda>(<lambda(int, int)>&&)
test1.cpp:17:14: note:   candidate expects 1 argument, 2 provided

我可以解决这个问题,添加两个小的更改,首先我需要将lambda更改为:

bool Less = [](int a,int b) -> bool
{
    return a < b;
}

正如您所看到的,我刚刚将auto替换为bool,仅此修改仍不起作用:

test1.cpp:13:21: error: expression list treated as compound expression in functional cast [-fpermiss
ive]
     return T(1,2);

除非我添加-fpermissive,否则我可能会以这种方式更改operator< bool

bool operator<(const foo<T>&) const
{
    return T((1,2));
}

注意双括号。现在代码编译,一切正常。

我的问题是,auto Less无效但bool Less工作的技术原因是什么?

我相信我知道为什么在第二个operator<中需要双括号,这应该是为了避免编译器将T(1,2)解释为声明而不是调用。

感谢您的时间

2 个答案:

答案 0 :(得分:7)

在您的第一个示例中,您正构建foo<T> [T = decltype(Less)]。所以在这个表达式中

return T(1,2);

你试图通过调用一个占用2 int s的构造函数来构造lambda的一个实例,这显然不存在。这正是错误消息告诉你的内容

error: no matching function for call to '<lambda(int, int)>::__lambda0(int, int)'

唯一存在的构造函数是lambda的复制和移动构造函数(从lambda表达式创建的闭包类型不是默认构造的),编译器尝试匹配参数并且失败

constexpr<lambda(int, int)>::<lambda>(const<lambda(int, int)>&)
constexpr<lambda(int, int)>::<lambda>(<lambda(int, int)>&&)
candidate expects 1 argument, 2 provided

在第二种情况下,通过进行此更改

bool Less = [](int a,int b) -> bool
{
    return a < b;
};

您所做的是声明一个名为Less的布尔变量,并将其初始化为true

这是因为你拥有的lambda表达式是无捕获的,这意味着它可以隐式转换为一个指向函数的指针,该函数接受与lambda operator()相同的参数并返回与原始相同的类型拉姆达。所以你将lambda表达式转换为bool(*)(int,int)

接下来,函数指针可以隐式转换为bool,并且将始终求值为true(假设它实际上指向函数的地址,它在此处执行)。因此Less初始化为true

现在,decltype(Less)只是bool。所以在这里你要尝试一个函数样式转换为bool,但传入2个参数

return T(1,2);

因此错误

error: expression list treated as compound expression in functional cast

通过添加额外的括号,您有一个由逗号运算符分隔的2个子表达式组成的表达式。这将评估并丢弃第一个子表达式(1)并返回第二个(2)的值,然后将其转换为bool,以便转换为true }。


我不确定你要尝试做什么才能建议一个解决方案,但是如果你想要的只是为foo定义一个比较谓词,那么'{priority_queue将会调用struct foo { foo(int v) {} }; auto Less = [](foo const& a, foo const& b) -> bool { return true; // do whatever comparison you need }; int main() { using my_priority_queue = std::priority_queue<foo, std::vector<foo>, decltype(Less)>; my_priority_queue data(Less); // pass a copy of the comparator } 。 1}},然后可能以下工作?

{{1}}

Live demo

答案 1 :(得分:1)

第一种情况:

声明

return T(1,2);

T是一种类型,lambda表达式的类型。 T(1, 2)尝试构建该类型的实例,我认为这是不可能的。这解释了编译器错误消息。我不知道你怎么可以只使用类型并调用该函数。

在第二种情况下,Tbool

bool(1, 2)不是有效的表达式。这解释了错误信息 bool((1, 2))bool(2),这是一个有效的表达式,其值为true