在运行时选择模板参数的 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/

相关文章:

c++ - 将数组写入 .hex 文件中的特定闪存位置

c++ - 如何在 C++ 中不使用 cin 扫描字符串?

C++ 在 int main() 之上声明模板 <Class T>

C++ 模板接口(interface)使用

c++ - 如何在 if 语句中结合否定和声明?

c++ - 在 apache 2.2 中正确使用 ap_set_content_type()

c++ - 相同的外部结构只有一个功能不同

arrays - 如何获取unique_ptr中存储的数组大小?

javascript - jQuery 模板 - 如何获取用户编辑输入的值

c++ - ctime 有问题,计算函数运行时间