在运行时选择模板参数的 C++ 函数

标签 c++ c++11 templates boost template-meta-programming

这个问题可能会为 another question 提供答案我最近发帖了,但我觉得这两个问题有很大的不同(这更笼统)。我也确实意识到有一些问题要求类似的东西,例如

但我找不到任何关于如何使用函数实现此调度的信息。

设置:

#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/

相关文章:

vue.js - 使用 VITE + Vue3 - [Vue 警告] : Component is missing template or render function

java - 查找给定输入的数据类型

带有 auto 关键字的 C++11 函数定义

c++ - 仅使用iostream按字母顺序对二维数组进行冒泡排序?

c++ - 在指定之前使用模板类

templates - 在 Twig 的父作用域中设置变量

python - 为什么当我使用 pybind11 为 C++ 库打包我的 python 绑定(bind)时出现 `ld: library not found for -lstdc++`

c++ - 为什么下面的代码不起作用?

c++ - 不能在 boost/unordered_map 中使用 vector

c++ - 为什么 std::stoul 转换负数?