30

我正在使用一个库,它公开了一个可以使用的接口。这个库的功能之一是这样的:

template <int a>
void modify(){}

我必须修改从 1 到 10 的参数,即使用从 1 到 10modify的模板参数调用。为此,我编写了这段代码(代码的基本版本,实际代码要大得多)。

for(int i=0; i<10; i++){
    modify<i>();
}

在编译时我收到以下错误

error: 'i' cannot appear in constant-expression

在浏览了互联网上的一些链接之后,我开始知道我不能将任何值作为模板参数传递,而模板参数在编译时不会被评估。我的问题如下: 1.为什么编译器不能i在编译时评估?2. 在不改变 API 接口的情况下,还有其他方法可以实现我想要实现的目标吗?


还有一件事我想做。调用 modify as modify 其中 VAR 是某些函数计算的输出。我怎样才能做到这一点?

4

6 回答 6

37

编译时i(不是常数)的值是多少?除非执行循环,否则无法回答。但是执行不是“编译”,因为没有答案,编译器无法做到这一点。

模板不是要执行的算法,而是要扩展以生成代码的宏。您可以做的是依靠专业化通过递归来实现迭代,如下所示:

#include <iostream>

template<int i>
void modify()
{ std::cout << "modify<"<<i<<">"<< std::endl; }

template<int x, int to>
struct static_for
{
    void operator()() 
    {  modify<x>();  static_for<x+1,to>()(); }
};

template<int to>
struct static_for<to,to>
{
    void operator()() 
    {}
};


int main()
{
    static_for<0,10>()();
}

请注意,通过这样做,您实际上是在实例化 10 个名为 modify<0>...的函数,它们modify<9>分别由static_for<0,10>::operator()...调用static_for<9,10>::operator()

迭代结束,因为static_for<10,10>将从采用两个相同值的特化中实例化,什么都不做。

于 2012-06-18T11:25:05.807 回答
3
  1. “为什么编译器不能i在编译时评估?”

    这将违背模板的目的。模板适用于源代码在某些情况下看起来相同的情况,但编译器需要生成的指令每次都不同。

  2. “在不更改 API 接口的情况下,还有其他方法可以实现我想要实现的目标吗?”

    是的,看看Boost.MPL

    但是我怀疑这里的正确答案是您想要更改 API。这取决于modify函数的内部结构。我知道你有它的来源,因为模板必须在标题中定义。所以看看为什么它需要i在编译时知道,如果不需要,最好用带参数的普通函数替换(或补充,如果你需要保持向后兼容性)。

于 2012-06-18T11:25:47.143 回答
3

由于您使用Boost.MPL要求答案:

#include <boost/mpl/for_each.hpp>
#include <boost/mpl/range_c.hpp>

#include <iostream>

template <int N>
void modify()
{
    std::cout << N << '\n';
}

// You need to wrap your function template in a non-template functor
struct modify_t
{
    template <typename N>
    void operator()(N)
    {
        modify<N::value>();
    }
};

int main()
{
    namespace mpl = boost::mpl;

    mpl::for_each< mpl::range_c<int,0,10> >( modify_t() ); // prints 0 to 9
}
于 2012-06-18T12:23:17.057 回答
1

不使用structBoost也可以这样做:

#include <iostream>
#include <utility>

template <int a>
void modify()
{
    std::cout<<a<<",";
}

template<int i,size_t... t>
constexpr inline void CT_for_impl(std::integer_sequence<size_t,t...>)
{
    bool kai[]= { (modify<i+t>(), false)...};
}

template<int i,int n>
constexpr inline void CT_for()
{
    CT_for_impl<i>(std::make_index_sequence<n-i+1>());
}

int main()
{
    CT_for<-5,5>();
    return 0;
}
于 2016-09-07T23:09:57.937 回答
1

错误解决方案:对于上述问题,'i' 不能出现在常量表达式中

要了解 constexpr,请单击此链接

#include <iostream>
using namespace std;

template <typename T>
void modify(T a)
{
    cout<<a<<endl;  //to check if its working 
}


//func converts int a into const int a
constexpr int func(int a)
{
    return a;
}

int main(){
    for(int i=0; i<10; i++){
        modify(func(i));//here passing func(i) returned value which can be used as template argument now as it is converted to constexpr    
}
    return 0;
}
于 2017-11-01T06:26:44.477 回答
1

鉴于您想通过索引在运行时调用函数并且您无法更改 API,您可以考虑类型擦除:

std::vector<std::function<void(int)> > func;
func.push_back(modify<1>);
func.push_back(modify<2>);
//... and so on ...
func.push_back(modify<10>);

for(int i=0; i<10; ++i)
{
    func[i]();  //calls modify<i+1>();
}

需要提及的几点:

  • 这不是模板的主要用途,但它是将静态库带入运行时世界的一种方式。对此的基本要求是使用同质类型(--如果modify<7>()会返回,比如说,std::string整个方法会中断)。
  • 先前使用类型擦除的解决方案有开销。使用函数指针可能会更快,但它总是比在编译时调用函数要慢。
  • 也可以(并且应该)将push_backs 包装到另一个迭代静态函数中,以避免手动调用。
于 2017-11-01T06:50:07.680 回答