Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 模拟运行时数值模板参数的习惯用法?_C++_Templates_Reflection_Idioms - Fatal编程技术网

C++ 模拟运行时数值模板参数的习惯用法?

C++ 模拟运行时数值模板参数的习惯用法?,c++,templates,reflection,idioms,C++,Templates,Reflection,Idioms,假设我们有 template <unsigned N> foo() { /* ... */ } 它调用相应的foo()变量。这不仅仅是一个合成的例子——这在实际生活中确实发生了(当然,不一定是虚空而无效的函数和仅仅一个模板参数),但我正在简化。当然,在C++中,我们不能有以下的: do_foo(unsigned n) { foo<n>(); } do_foo(无符号n){foo();} 我现在做的是 do_foo(unsigned n) { switch(

假设我们有

template <unsigned N> foo() { /* ... */ }
它调用相应的
foo()
变量。这不仅仅是一个合成的例子——这在实际生活中确实发生了(当然,不一定是虚空而无效的函数和仅仅一个模板参数),但我正在简化。当然,在C++中,我们不能有以下的:

do_foo(unsigned n) { foo<n>(); }
do_foo(无符号n){foo();}
我现在做的是

do_foo(unsigned n) { 
    switch(n) {    
    case n_1: foo<n_1>(); break;
    case n_2: foo<n_2>(); break;
    /* ... */
    case n_k: foo<n_k>(); break;
    }
}
do_foo(无符号n){
开关(n){
案例n_1:foo();break;
案例2:foo();break;
/* ... */
案例n_k:foo();break;
}
}
当我知道n有效地限制在n_1,…,n_k的范围内时,但这是不恰当的,而且当调用较长时,我需要多次复制长序列的模板和常规参数时,情况更是如此

<>我正准备在一个宏上生成这些转换语句,当我开始思考,也许有人已经在某个库中工作过,并且可以共享他们所做的事情。如果不是,也许仍然有一个C++函数构造,它可以任意的函数,任何模板和非模板的序列。包含一些数字模板参数和某种形式的值序列的ters,以生成一个包装器,该包装器可以将该模板参数作为附加的运行时参数,例如

auto& transformed_foo = magic<decltype(foo)>(foo)::transformed;
auto&transformed\u foo=magic(foo)::transformed;

为了简化此过程,我将围绕
foo
制作一个函子包装:

struct Foo {
    template <unsigned N>
    void operator()(std::integral_constant<unsigned,N>)
    { foo<N>(); }
};
这当然可以变得更通用,但这应该为您应用到问题领域提供一个良好的起点


这是@TartanLlama针对无参数函数的解决方案对具有任意数量参数的函数的扩展。它还具有规避GCC错误(版本8之前)的额外好处,即当扩展为lambda时,无法正确扩展可变模板参数包

#include <iostream>
#include <utility>
#include <array>
#include <functional>

struct Foo {
    template <std::size_t N, typename... Ts> void operator()(std::integral_constant<std::size_t,N>, Ts... args)
    { foo<N>(std::forward<Ts>(args)...); }
};

template <std::size_t N, typename F, typename... Ts>
std::function<void(Ts...)> make_visitor(F f) {
    return 
        [&f](Ts... args) {
            f(std::integral_constant<std::size_t,N>{}, std::forward<Ts>(args)...);
        };
}

template <std::size_t Offset, std::size_t... Idx, typename F, typename... Ts>
void visit(F f, std::index_sequence<Idx...>, std::size_t n, Ts... args) {
    static std::array<std::function<void(Ts...)>, sizeof...(Idx)> funcs {{
        make_visitor<Idx+Offset, F, Ts...>(f)...
    }};
    funcs[n-Offset](std::forward<Ts>(args)...);
};

template <std::size_t Start, std::size_t End, typename F, typename... Ts>
void visit(F f, std::size_t n, Ts... args) {
    visit<Start>(f, std::make_index_sequence<End-Start>{}, n, std::forward<Ts>(args)...);
};
#包括
#包括

#include

虽然其他两个答案相当通用,但编译器很难对它们进行优化。我目前在一个非常类似的情况下使用以下解决方案:

#include <utility>
template<std::size_t x>
int tf() { return x; }

template<std::size_t... choices>
std::size_t caller_of_tf_impl(std::size_t y, std::index_sequence<choices...>) {
  std::size_t z = 42;
  ( void( choices == y && (z = tf<choices>(), true) ), ...);
  return z;
}

template<std::size_t max_x, typename Choices = std::make_index_sequence<max_x> >
std::size_t caller_of_tf(std::size_t y) {
  return caller_of_tf_impl(y, Choices{});
}

int a(int x) {
  constexpr std::size_t max_value = 15;
  return caller_of_tf<max_value+1>(x);
}
#包括
模板
int tf(){return x;}
模板
std::size\u t调用方\u of theu tf\u impl(std::size\u t y,std::index\u序列){
标准:尺寸_t z=42;
(void(choices==y&&(z=tf(),true)),…);
返回z;
}
模板
std::size\u t调用方\u of \u tf(std::size\u t y){
返回调用方\(y,选项{});
}
整数a(整数x){
constexpr std::size\u t max\u value=15;
返回_tf(x)的调用方_;
}
其中我们有一些模板化函数
tf
,出于说明性原因,它只返回其模板参数和函数
调用者
它希望调用适当的
tf
给定一个运行时参数
y
。它基本上依赖于首先构造一个大小合适的参数包,然后使用短路
&
运算符扩展此参数包,该运算符严格地只在第一个参数为tru时计算其第二个参数e、 然后,我们只需将运行时参数与参数包的每个元素进行比较


这个解决方案的优点在于它可以直接进行优化,例如,turns
a()
检查
x
是否小于16,并返回该值。该值略低于最佳值,但仍设法仅使用if-else链。对einpoklum发布的解决方案执行相同操作将导致生成更多程序集(例如,使用)。当然,缺点是上面的解决方案更为具体。

有什么东西阻止你把它变成一个
constepr
函数吗?比如。@TartanLlama:你对我的问题想的方向不对。我不想在编译时使用额外的参数,我想在运行时用一个编译的函数来获得更大的灵活性基于N.Cool的值,这是非常不同的。所以我猜你有一系列专门化,阻止你简单地将它变成
constepr
。你可能可以在运行时做一些类似于索引
std::tuple
s的技巧。例如,请看。如果我有时间,我会尝试编写一个解决方案。类似于?@TartanLlam答:现在你说的是…索引序列和省略号扩展。很好。但是也许你应该举个例子来强调参数是如何在编译时确定的。这很好。但是-1。我不是第一个想这样做的人。这样的习惯用法不是某个库的一部分吗?某些TS或Boost中的某个地方吗?2.为什么对象是wr似乎有必要?不能使用函数指针吗?@einpoklum 1.也许,但我不知道有一个。2.使用函数指针的问题是,您需要解决在
访问的调用站点上指向哪个函数,因此它实际上不起作用。您可能会想出一种在C++14.I中使用通用lambda的方法我只是想使用它,但注意到它既不是用GCC 6.x编译,也不是用7.x编译(!!)它是用clang 6.0(可能更早),还有GCC 8.x编译的。此外,这段代码是C++11(除了缺少的
std::index_序列
,您需要用自己的序列替换).这里也没有真正的理由使用
std::function
visit<0, 10>(Foo{}, i);
// min^  ^max       
visit<Start>(f, n, std::make_index_sequence<End-Start>{});
template <std::size_t Offset, std::size_t... Idx, typename F>
void visit(F f, std::size_t n, std::index_sequence<Idx...>) {
    std::array<std::function<void()>, sizeof...(Idx)> funcs {{
        [&f](){f(std::integral_constant<unsigned,Idx+Offset>{});}...
    }};

    funcs[n - Offset]();
};
#include <iostream>
#include <utility>
#include <array>
#include <functional>

struct Foo {
    template <std::size_t N, typename... Ts> void operator()(std::integral_constant<std::size_t,N>, Ts... args)
    { foo<N>(std::forward<Ts>(args)...); }
};

template <std::size_t N, typename F, typename... Ts>
std::function<void(Ts...)> make_visitor(F f) {
    return 
        [&f](Ts... args) {
            f(std::integral_constant<std::size_t,N>{}, std::forward<Ts>(args)...);
        };
}

template <std::size_t Offset, std::size_t... Idx, typename F, typename... Ts>
void visit(F f, std::index_sequence<Idx...>, std::size_t n, Ts... args) {
    static std::array<std::function<void(Ts...)>, sizeof...(Idx)> funcs {{
        make_visitor<Idx+Offset, F, Ts...>(f)...
    }};
    funcs[n-Offset](std::forward<Ts>(args)...);
};

template <std::size_t Start, std::size_t End, typename F, typename... Ts>
void visit(F f, std::size_t n, Ts... args) {
    visit<Start>(f, std::make_index_sequence<End-Start>{}, n, std::forward<Ts>(args)...);
};
#include <utility>
template<std::size_t x>
int tf() { return x; }

template<std::size_t... choices>
std::size_t caller_of_tf_impl(std::size_t y, std::index_sequence<choices...>) {
  std::size_t z = 42;
  ( void( choices == y && (z = tf<choices>(), true) ), ...);
  return z;
}

template<std::size_t max_x, typename Choices = std::make_index_sequence<max_x> >
std::size_t caller_of_tf(std::size_t y) {
  return caller_of_tf_impl(y, Choices{});
}

int a(int x) {
  constexpr std::size_t max_value = 15;
  return caller_of_tf<max_value+1>(x);
}