模板函数的动态调度

Dynamic dispatching of template functions?

本文关键字:动态调度 函数      更新时间:2023-10-16

是否可以在运行时决定调用哪个模板函数?类似于:

template<int I>
struct A {
    static void foo() {/*...*/}
};
void bar(int i) {
    A<i>::f();   // <-- ???
}

在处理模板时,连接编译时和运行时的一个典型"技巧"是访问变体类型。例如,Generic Image Library(可作为Boost.GIL或单机版提供(就是这样做的。它通常采用以下形式:

typedef boost::variant<T, U, V> variant_type;
variant_type variant = /* type is picked at runtime */
boost::apply_visitor(visitor(), variant);

其中visitor是简单地转发到模板的多态函子:

struct visitor: boost::static_visitor<> {
    template<typename T>
    void
    operator()(T const& t) const
    { foo(t); } // the real work is in template<typename T> void foo(T const&);
};

这有一个很好的设计,即模板将/可以实例化的类型列表(这里是variant_type类型同义词(不与代码的其余部分耦合。像boost::make_variant_over这样的元函数也允许对要使用的类型列表进行计算。

由于这种技术不适用于非类型参数,因此您需要手动"展开"访问,不幸的是,这意味着代码不那么可读/可维护。

void
bar(int i) {
    switch(i) {
        case 0: A<0>::f(); break;
        case 1: A<1>::f(); break;
        case 2: A<2>::f(); break;
        default:
            // handle
    }
}

处理上述切换中重复的常见方法是(ab(使用预处理器。使用Boost的(未经测试的(示例。预处理器:

#ifndef LIMIT
 #define LIMIT 20 // 'reasonable' default if nothing is supplied at build time
#endif
#define PASTE(rep, n, _) case n: A< n >::f(); break;
void
bar(int i) {
    switch(i) {
        BOOST_PP_REPEAT(LIMIT, PASTE, _)
        default:
            // handle
    }
}
#undef PASTE
#undef LIMIT

最好为LIMIT找到好的、自文档化的名称(对PASTE也没有坏处(,并将上面的代码生成限制在一个站点。


根据David的解决方案和您的意见构建:

template<int... Indices>
struct indices {
    typedef indices<Indices..., sizeof...(Indices)> next;
};
template<int N>
struct build_indices {
    typedef typename build_indices<N - 1>::type::next type;
};
template<>
struct build_indices<0> {
    typedef indices<> type;
};
template<int... Indices>
void
bar(int i, indices<Indices...>)
{
    static void (*lookup[])() = { &A<Indices>::f... };
    lookup[i]();
}

然后调用bar:bar(i, typename build_indices<N>::type()),其中N将是您的常数时间常数sizeof...(something)。你可以添加一层来隐藏这个调用的"丑陋":

template<int N>
void
bar(int i)
{ bar(i, typename build_indices<N>::type()); }

称为CCD_ 10。

您可以创建一个查找表,然后动态使用它,具体取决于您想要做什么(即是否有少量有限的实例化需要使用?(。对于完全手动的方法,使用选项0、1、2和3,您可以执行以下操作:

void bar( int i ) {
   static void (*lookup[])(void) = { &A<0>::foo, &A<1>::foo, &A<2>::foo, &A<3>::foo };
   lookup[i]();
}

当然,我为这个例子选择了最简单的选项。如果所需的数字不是连续的或基于零,则可能更喜欢std::map<int, void (*)(void) >而不是数组。如果要使用的不同选项的数量较大,则可能需要添加代码来自动输入模板,而不是手动键入所有模板。。。但是您必须考虑模板的每个实例化都会创建一个新的函数,并且您可能需要检查是否确实需要它。

EDIT:我写了一篇文章,只使用C++03功能实现了相同的初始化,这篇文章似乎太长了,无法给出答案。

Luc Danton在这里写了一个有趣的答案,其中包括使用C++0x结构初始化查找表。我不太喜欢这个解决方案,因为它更改了接口以需要额外的参数,但这可以通过中间调度器轻松解决。

不,模板是编译时的特性,而i在编译时是未知的,所以这是不可能的。CCD_ 13应该适应于类似CCD_。


模板实现编译时多态性,而不是运行时多态性。

模板参数必须在编译时已知。所以编译器无法通过A<i>::foo()

如果你想变通,那么你必须使bar()也成为template:

template<int i>
void bar() {
    A<i>::f();   // ok
}

为此,您必须在编译时知道bar()的参数。