为什么clang ++不能推断出lambda地图的类型?

时间:2016-07-28 09:47:11

标签: c++ lambda c++14 auto

我有以下代码:

enum RelationalOperator { LT, LTE, EQ, GTE, GT };
std::map<RelationalOperator, bool (*)(const Point&, const Point&)> ops = {
    { GTE, [](const Point& a, const Point& b) { return a >= b; } },
    { LTE, [](const Point& a, const Point& b) { return a <= b; } },
    { EQ, [](const Point& a, const Point& b) { return a == b; } },
    { GT, [](const Point& a, const Point& b) { return a > b; } },
    { LT, [](const Point& a, const Point& b) { return a < b; } },
};

此代码位于模板中,Point是模板参数。

我尝试用ops替换变量auto的类型,但Clang ++说:

src/utils.hpp:47:10: error: cannot deduce actual type for variable 'ops' with type 'auto' from initializer list

为什么?我认为关键字auto适用于这种情况,其中类型很长且相当明显。

4 个答案:

答案 0 :(得分:5)

ALTER TABLE `tbl_name` ADD `cdate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; 不适用于初始化列表。可以使用相同的初始化列表初始化一堆其他类型,例如:

auto

答案 1 :(得分:4)

首先,每个lambda都有自己的类型,因此给定一组不同的lambda,如果没有一些手动转换(通常将它们嵌入std::function<R(Args...)>个对象中),就不能将它们分解为单个类型。

然后,当你编写这样的初始化时:

enum RelationalOperator { LT, LTE, EQ, GTE, GT };
std::map<RelationalOperator, bool (*)(const Point&, const Point&)> ops = {
    { GTE, [](const Point& a, const Point& b) { return a >= b; } },
    { LTE, [](const Point& a, const Point& b) { return a <= b; } },
    { EQ, [](const Point& a, const Point& b) { return a == b; } },
    { GT, [](const Point& a, const Point& b) { return a > b; } },
    { LT, [](const Point& a, const Point& b) { return a < b; } },
};

真正发生了什么?它调用std::initializer_list的{​​{1}}构造函数。它还能够推断出给定的支撑表达式是这种地图的初始化列表:

std::map<RelationalOperator, bool (*)(const Point&, const Point&)>

然后,你的lambda会发生隐式转换。

现在,如果你改写:

std::initializer_list<std::pair<RelationalOperator, bool (*)(Point const&, Point const&)>>

它无法确定支撑表达式(auto ops = { { GTE, [](const Point& a, const Point& b) { return a >= b; } }, { LTE, [](const Point& a, const Point& b) { return a <= b; } }, { EQ, [](const Point& a, const Point& b) { return a == b; } }, { GT, [](const Point& a, const Point& b) { return a > b; } }, { LT, [](const Point& a, const Point& b) { return a < b; } }, }; 中的T)表示的对象类型。这在gcc's error message中非常明确:

std::initializer_list<T>

答案 2 :(得分:3)

  

我认为关键字auto适用于这类情况,其中类型很长且相当明显。

类型并不明显。每个lambda表达式都会生成一个唯一的匿名闭包类型,因此初始化列表的每个元素都有不同的类型:

auto ops = {
    { GTE, lambda_type_1 },
    { LTE, lambda_type_2 },
    { EQ, lambda_type_3 },
    { GT, lambda_type_4 },
    { LT, lambda_type_5 },
};

每个支撑的初始化器没有任何共同之处。没有什么明显的。

初始化std::map时,有一个构造函数使用std::initializer_list<value_type>,编译器可以将每个初始值设定项转换为该类型。当您使用auto替换地图时,编译器无法确定您希望从不相关类型列表中推断出哪种类型。

答案 3 :(得分:1)

还可以考虑使用std::equal_to中可用的STL内置比较仿函数std::less_equalstd::greater_equalstd::lessstd::greater<functional>头文件。

E.g:

#include <functional>
#include <map>

struct Point {
   bool operator <  (const Point &) const { /* actual implementation */ }
   bool operator <= (const Point &) const { /* actual implementation */ }
   bool operator >  (const Point &) const { /* actual implementation */ }
   bool operator >= (const Point &) const { /* actual implementation */ }
   bool operator == (const Point &) const { /* actual implementation */ }
   /* other stuff */
};

int main() {
   enum RelationalOperator { LT, LTE, EQ, GTE, GT };
    std::map<RelationalOperator, std::function<bool(const Point&, const Point&)>> ops = {
    {GTE, std::greater_equal<Point>()},
    {LTE, std::less_equal<Point>()},
    {EQ, std::equal_to<Point>()},
    {GT, std::greater<Point>()},
    {LT, std::less<Point>()},
   };
}