我以为我会对一些排序算法进行基准测试,但我必须正确地做模板:
#include <iostream>
#include <vector>
template <typename ForwardIterator>
void dummysort(ForwardIterator begin_it, ForwardIterator end_it)
{
// pretend to use these and sort stuff
++begin_it;
++end_it;
}
template<typename SortFunc>
void benchmark(const char* name, SortFunc sort_func, std::vector<int> v)
{
std::cout << name << std::endl;
sort_func(v.begin(), v.end());
}
int main()
{
std::vector<int> first = {3, 2, 1};
benchmark("bubblesort", dummysort, first);
}
10:48 $ clang -std=c++14 tmp.cpp
tmp.cpp:30:5: error: no matching function for call to 'benchmark'
benchmark("bubblesort", dummysort, first);
^~~~~~~~~
tmp.cpp:20:6: note: candidate template ignored: couldn't infer template argument 'SortFunc'
void benchmark(const char* name, SortFunc sort_func, std::vector<int> v)
^
1 error generated.
10:47 $ clang --version
Apple LLVM version 8.1.0 (clang-802.0.42)
Target: x86_64-apple-darwin16.0.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/
如果我&#34; untemplate&#34; dummysort
,它有效。
void dummysort(std::vector<int>::iterator begin_it, std::vector<int>::iterator end_it)
有没有办法可以让它一般地运作,或者,如果没有,有人可以给我一个类似于this answer的好解释或思考实验吗?
答案 0 :(得分:4)
正如nwp所解释的,模板函数不是函数;只是构建函数的一个方法。
所以你不能传递食谱
benchmark("bubblesort", dummysort, first);
但是你可以用它来解释从配方中构造函数的类型;我的意思是
benchmark("bubblesort", dummysort<std::vector<int>::iterator>, first);
有效,但我发现它有点难看。
我建议你另一个解决方案:使用模板operator()
构建一个类(或结构),而不是模板函数;
struct dummySort
{
template <typename FI>
void operator() (FI begin_it, FI end_it)
{
++begin_it;
++end_it;
}
};
通过这种方式,您可以调用benchmark()
传递此类型的对象而不解释迭代器的类型:它是benchmark()
内部对运算符的调用,它选择正确的类型并构造正确的运算符
以下是完整的编译示例
#include <iostream>
#include <vector>
struct dummySort
{
template <typename FI>
void operator() (FI begin_it, FI end_it)
{ ++begin_it; ++end_it; }
};
template <typename SortFunc>
void benchmark (char const * name, SortFunc sort_func, std::vector<int> v)
{
std::cout << name << std::endl;
sort_func(v.begin(), v.end());
}
int main()
{
std::vector<int> first = {3, 2, 1};
benchmark("bubblesort", dummySort{}, first);
}
答案 1 :(得分:1)
解决此问题有几种方法。
您可以显式指定static_cast
的模板参数,也可以struct dummysort_t {
template <typename ForwardIterator>
void operator()(ForwardIterator begin_it, ForwardIterator end_it) const {
/* ... */
}
} constexpr dummysort{}; // in C++17, also mark inline
指定相应的函数指针类型。对我来说,这是一个非常糟糕的解决方案,因为它锁定了特定的重载选择而不是依赖于语言的重载规则。如果您更改其他参数或类型,您可以获得(最佳情况)损坏代码或(最坏情况)代码,但可以执行某些操作但
您可以将功能模板提升为具有呼叫操作员模板的对象功能:
dummysort
这使您可以直接将dummysort
传递到具有相同预期行为的所有功能模板。这个解决方案的缺点是,如果你不拥有dummysort
,这当然是一个非首发,如果你使用[](auto b, auto e) { dummysort(b, e); }
作为自定义点,你需要做更多的工作让它具有预期的行为。
你可以用lambda包装它。因为我们正在复制迭代器,所以简单的形式就可以了:
dummysort
这会延迟#define OVERLOADS_OF(name) [&](auto&&... args) -> decltype(name(std::forward<decltype(args)>(args)...)) { return name(std::forward<decltype(args)>(args)...); }
的模板实例化,直到另一个函数模板实际调用它为止。这为您提供了您想要的确切行为,并且可以在所有情况下工作(并修复了#2的问题)。更一般地说,这种模式应该像:
OVERLOADS_OF(dummysort)
你将通过:
git rebase --onto <commitId>
对于这种特殊情况来说是过度杀伤,但更普遍有用。
答案 2 :(得分:0)
两条小评论:您应该通过引用v
而不是benchmark
将std::vector<int> &v
参数传递给std::vector<int> v
,因为向量是构建的昂贵对象。标准流不需要std::endl
;而不是
std::cout << "Value of the variable:" << std::endl;
std::cout << variable << std::endl;
你可以写
std::cout << "Value of the variable:\n";
std::cout << variable << '\n';
这两者都不那么冗长和快速(不是因为这里的琐碎案例很重要)。
要回答你的问题,不,编译器在这种情况下无法推断dummysort
的模板参数,因此你必须将其编写为dummysort<std::vector<int>::iterator>
。但有一些方法可以简化它:
1)如果您的编译器支持C ++ 14 variable templates,您可以声明
template<typename T>
constexpr auto vecdummysort = dummysort<typename std::vector<T>::iterator>;
然后像
一样使用它benchmark("bubblesort", vecdummysort<int>, first);
2a)使用C ++ 11,您可以创建alias template
template<typename T>
using veciter = typename std::vector<T>::iterator;
允许您使用dummysort
之类的
benchmark("bubblesort", dummysort<veciter<int>>, first);
2b)对于与示例#1中相同的vecdummysort
函数:
template<typename T>
void vecdummysort(veciter<T> first, veciter<T> last) {dummysort(first, last);}
3)您也可以将sort_func
中的benchmark
调用更改为使用原始指针而不是迭代器,所以
sort_func(v.data(), v.data() + v.size());
而不是
sort_func(v.begin(), v.end());
这意味着您可以使用benchmark
之类的
benchmark("bubblesort", dummysort<int*>, first);
4)您可以更改dummysort
的接口以将集合本身作为参数而不是迭代器,这会牺牲一点点灵活性(例如,如果您只想对集合的一部分进行排序,或者使用反向迭代器等等,以获得更简洁,更符合人体工程学的界面
template<typename Collection>
void dummysort(Collection &c)
{
auto first = std::begin(c);
auto last = std::end(c);
}
用法:
// benchmark()
sort_func(v);
// main()
benchmark("bubblesort", dummysort<std::vector<int>>, first);