这个问题可能会为 another question 提供答案我最近发帖了,但我觉得这两个问题有很大的不同(这更笼统)。我也确实意识到有一些问题要求类似的东西,例如
- Specify template parameters at runtime
- Select template argument at runtime in C++
- How to instantiate c++ template according to runtime input?
- Select template argument at runtime in C++
但我找不到任何关于如何使用函数实现此调度的信息。
设置:
#include <vector>
#include <iostream>
template <class T>
size_t length(size_t len) {
std::vector<T> vec(len, 100);
return vec.size();
}
size_t length(size_t type, size_t len) {
switch(type) {
case 0: return length<int>(len);
case 1: return length<double>(len);
default: throw std::runtime_error("error");
}
}
int main () {
std::cout << "length: " << length(0, 4) << "\n";
std::cout << "length: " << length(1, 5) << "\n";
return 0;
}
我想写一个函数 dispatch(type, fun, ...)
来实现这个开关
auto dispatch(type, fun, ...) -> decltype(fun<int>(...)) {
switch(type) {
case 0: return fun<int>(...);
case 1: return fun<double>(...);
default: throw std::runtime_error("error");
}
}
到目前为止,我能够想出两种方法来解决这个问题:
仿函数方法:
template <template<typename> class Func, typename ...Ar>
auto dispatch_type(size_t type, Ar&&... rg) ->
decltype(Func<int>()(std::forward<Ar>(rg)...)) {
switch(type) {
case 0: return Func<int>()(std::forward<Ar>(rg)...);
case 1: return Func<double>()(std::forward<Ar>(rg)...);
default: throw std::runtime_error("error");
}
}
template <class T>
struct Length {
size_t operator()(size_t len) {
std::vector<T> vec(len, 100);
return vec.size();
}
};
size_t length(size_t type, size_t len) {
return dispatch_type<Length>(type, len);
}
使用 boost::mp11
:
#include <boost/mp11/list.hpp>
#include <boost/mp11/algorithm.hpp>
namespace mp11 = boost::mp11;
using num_types = mp11::mp_list<int, double>;
template <size_t i>
using num_type = mp11::mp_at_c<num_types, i>
template<class F>
inline constexpr
decltype(std::declval<F>()(std::declval<mp11::mp_size_t<0>>()))
with_type(std::size_t i, F && f) {
return mp11::mp_with_index< mp11::mp_size<num_types> >(i,
std::forward<F>(f));
}
size_t length(size_t i, size_t len) {
return with_type(i, [&](auto I) {
std::vector< num_type<I> > vec(len, 100);
return vec.size();
});
}
我很惊讶这很难实现。从概念上讲,我的问题很简单:给定一个函数模板,确保存在一组类型(在编译时已知)的显式实例化,并在运行时基于开关分派(dispatch)适当的实例。
除了提议的两个选项之外,还有哪些其他选项?对于我的应用程序,我仅限于使用 C++11,但在这个问题的上下文中,
最佳答案
你可以通过简单地将你的类型移动到一个类型列表中,并使用旧式递归来做到这一点
#include <stdexcept>
// terminating case to avoid if-constexpr
template <template<class> class F,
typename Ret>
Ret dispatch(int)
{
// it doesn't matter what the argument is, we're out of types ...
throw std::runtime_error("error");
}
// main recursive case
template <template<class> class F,
typename Ret,
typename Arg,
typename... Args>
Ret dispatch(int index)
{
if(index == 0)
{
return F<Arg>::f();
}
else
{
return dispatch<F, Ret, Args...>(index-1);
}
}
template <typename T> struct foo;
template <> struct foo<int> { static int f(){return 1;} };
template <> struct foo<char> { static int f(){return 2;} };
并称它为
int main(void)
{
return dispatch<foo, int, int, char>(1);
// ^ ^
// type_0, type_1, ...
}
您可以通过将参数类型列表包装成一个元组并将其作为参数传递(如果需要,我们只是从参数中推导出类型列表,但它将它与返回类型参数分开)来使其更易于阅读在调用代码中)。
我们还可以从包装函数中的 F 推导出返回类型,再次稍微清理一下调用站点:
template <template<class> class F,
typename Arg,
typename... Args>
auto clean_dispatch(int index, std::tuple<Arg, Args...> const &)
{
using Ret = decltype(F<Arg>::f());
return dispatch<F, Ret, Arg, Args...>(index);
}
int main(void)
{
using MyTypes = std::tuple<int, char>;
return clean_dispatch<foo>(1, MyTypes{});
}
关于在运行时选择模板参数的 C++ 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57355728/