从带有lambda的std::函数返回类型的模板参数类型推导
template argument type deduction from std::function return type with lambda
首先,我使用的是C++11(我的主题很糟糕)。
我要做的是编写一个通用的模板函数,该函数在其他编程语言中通常被称为sort_by
。它包括为一个范围的每个成员精确计算一次任意标准,然后根据这些标准对该范围进行排序。这样的标准不一定是POD,它所要做的就是不具有可比性。对于std::less
不起作用的东西,调用者应该能够提供自己的比较函子。
我已经成功地编写了使用以下签名的函数:
template< typename Tcriterion
, typename Titer
, typename Tcompare = std::less<Tcriterion>
>
void
sort_by(Titer first, Titer last,
std::function<Tcriterion(typename std::iterator_traits<Titer>::value_type const &)> criterion_maker,
Tcompare comparator = Tcompare()) {
}
它可以像这样使用:
struct S { int a; std::string b; double c; };
std::vector<S> s_vec{
{ 42, "hello", 0.5 },
{ 42, "moo!", 1.2 },
{ 23, "fubar", 0.2 },
};
sort_by1< std::pair<int, double> >(
s_vec.begin(), s_vec.end(),
[](S const &one_s) { return std::make_pair(one_s.a, one_s.c); }
);
我不喜欢这种方法的地方在于,我必须自己提供Tcriterion
参数,因为编译器无法从lambda表达式推导出该类型。因此,这不起作用:
sort_by1(s_vec.begin(), s_vec.end(), [](S const &one_s) { return std::make_pair(one_s.a, one_s.c); });
clang 3.1和gcc 4.7.1都对此表示不满(gcc 4.7.11甚至对上面的代码表示不满,所以我想我在这里真的做错了什么)。
然而,如果我首先将lambda分配给std::function
,那么至少clang 3.1可以推导出该参数,这意味着它有效:
typedef std::pair<int, double> criterion_type;
std::function<criterion_type(S const &)> criterion_maker = [](S const &one_s) {
return std::make_pair(one_s.a, one_s.c);
};
sort_by1(s_vec.begin(), s_vec.end(), criterion_maker);
所以我的问题是:我必须如何更改我的函数签名,这样我就不需要指定那个参数了?以及(可能是相关的)我将如何修复我的示例以使其与gcc一起工作?
不要将std::function
与模板参数推导一起使用。事实上,很可能没有理由在函数或函数模板参数列表中使用std::function
。通常情况下,您不应该使用std::function
;它是一种非常专业的工具,非常擅长解决一个特定的问题。在剩下的时间里,你可以完全放弃它。
在您的情况下,如果您使用多态函子排序,则不需要模板参数推导:
struct less {
template<typename T, typename U>
auto operator()(T&& t, U&& u) const
-> decltype( std::declval<T>() < std::declval<U>() )
{ return std::forward<T>(t) < std::forward<U>(u); }
// operator< is not appropriate for pointers however
// the Standard defines a 'composite pointer type' that
// would be very helpful here, left as an exercise to implement
template<typename T, typename U>
bool operator()(T* t, U* u) const
{ return std::less<typename std::common_type<T*, U*>::type> {}(t, u); }
};
然后您可以申报:
template<typename Iter, typename Criterion, typename Comparator = less>
void sort_by(Iter first, Iter last, Criterion crit, Comparator comp = less {});
并且comp(*ita, *itb)
将做正确的事情,以及comp(crit(*ita), crit(*itb))
或任何其他事情,只要它有意义。
这样的东西怎么样:
template< typename Titer
, typename Tmaker
, typename Tcompare
>
void
sort_by(Titer first, Titer last,
Tmaker criterion_maker,
Tcompare comparator)
{
typedef decltype(criterion_maker(*first)) Tcriterion;
/*
Now that you know the actual type of your criterion,
you can do the real work here
*/
}
问题是,显然不能使用默认值作为比较器,但可以通过提供一个不使用比较器并在内部填充std::less
的重载来轻松克服这一问题。
要像您最初建议的那样完成,编译器必须能够"反转"模板实例化过程。即,对于给定的std::函数<>实例化,我必须提供什么参数作为结果才能得到它。这"看起来"很容易,但事实并非如此!
您也可以使用类似的东西。
template< typename Titer
, typename Tmaker
, typename TCriterion = typename
std::result_of
<
Tmaker
(
decltype(*std::declval<Titer>())
)
>::type
, typename Tcompare = std::less<TCriterion>
>
void
sort_by(Titer first, Titer last,
Tmaker criterion_maker, Tcompare comparator = Tcompare())
{
}
http://liveworkspace.org/code/0aacc8906ab4102ac62ef0e45a37707d
- 扩展C++生成的代码的模板参数类型名称
- 如何在 c++ 中定义接受不同参数类型的函数向量?
- 在 C++ 中运行时调用模板时,是否可以切换模板的参数类型?
- 将函数参数类型声明为 auto
- 将函数的参数 - 签名从使用 'std::function<T>' 转换为模板参数类型
- 在 C++17 中调用具有不同参数类型的构造函数
- 具有先前参数类型匹配的参数包
- 我想知道为什么"std::unique_ptr<int> foo(新 int)"是合法的,因为"std::<int>unique_ptr"要求输入参数类型应该是"int"?
- 将可变参数类型列表的扩展打包为复杂类型的初始值设定项列表 - 合法吗?
- MSVC 错误:4 个重载中的任何一个都无法转换所有参数类型
- 使用constexpr + auto作为返回和参数类型的奇怪类型推导
- 如何从第一个参数推断第二个参数类型?
- C++模板函数中,指定回调函子/lambda 的参数类型,同时仍允许内联?
- 如何用不同的参数类型和数字回调函数
- C++stoi:这两个重载都无法转换所有参数类型
- 为什么std::{container}::template不能推导其参数类型
- 为模板参数类型中的新对象分配内存
- 为指向成员模板参数的指针推导额外模板参数类型的紧凑方式
- 使用std::conditional中的模板来确定函数参数类型
- C++中的短参数类型