2012-12-11 35 views
15

这样的事情有可能存在吗?是否有可能在C++中开发静态循环?

template<int Channel> 
void deduce_mask(Matrix const &src, int mask[]) 
{ 
    //I hope i could become a constant and the compiler would unroll the loop at compile time   
    for(int i = Channel; i != -1; --i) 
    {    
     //mapper is a helper class which translate two and three dimension into one dimension index 
     //constexpr makes it possible to find out the index at compile time 
     mask[mapper(0, 1, i)] = src(row - 1, col)[i]; 
     mask[mapper(1, 1, i)] = src(row, col)[i]; 
     mask[mapper(2, 1, i)] = src(row + 1, col)[i];  
    } 
} 

代替

template<int Channel> 
class deduceMask 
{ 
public: 
    static void deduce_mask(matrix const &src, int mask[]); 
}; 

template<int Channel> 
void deduce_mask(matrix const &src, int mask[]) 
{     
    mask[mapper(0, 1, Channel)] = src(row - 1, col)[Channel]; 
    mask[mapper(1, 1, Channel)] = src(row, col)[Channel]; 
    mask[mapper(2, 1, Channel)] = src(row + 1, col)[Channel];  

    deduceMask<Channel - 1>::deduce_mask(src, mask); 
} 

template<> 
class deduceMask<-1> 
{ 
public: 
    static void deduce_mask(matrix const &src, int mask[]) 
    { 

    } 
}; 

第二种解决方案是唯一的解决办法我能想出的时候我想的编译器来找出 在编译time.Do结果我有一个简单的方法来使“i”变成恒定值,如 元编程解决方案?对我来说,一个简单的for循环更容易处理,而不是元编程版本的 。

对不起,我可怜的英语,我希望我能正确解释我的问题。

+2

你也可以递归地编写它并使用constexpr,如果你喜欢这种类型的语法? – Agentlien

+0

我试图做一个constexpr版本但失败,constexpr只允许一个return语句。 – StereoMatching

+3

我相当肯定,大多数现代编译器会自动执行这种优化,就像他们为'for'循环执行直到一个常量值(例如'for(int i = 0; i <5; i ++)')一样。你必须检查确定。 – ShdNx

回答

20

C++中的模板元编程是纯粹的函数式编程,而在纯函数式编程中,您不会像使用或使用循环一样使用循环,而且根本没有任何可变数据。你所有的只是递归。为了使递归工作更容易,你需要提高抽象层次。您有递归的代码是好的,但迭代和工作可以分道扬镳:

template <int First, int Last> 
struct static_for 
{ 
    template <typename Fn> 
    void operator()(Fn const& fn) const 
    { 
     if (First < Last) 
     { 
      fn(First); 
      static_for<First+1, Last>()(fn); 
     } 
    } 
}; 

template <int N> 
struct static_for<N, N> 
{ 
    template <typename Fn> 
    void operator()(Fn const& fn) const 
    { } 
}; 

既然你有这样的元函数,你可以写你的deduce_mask功能是这样的:

template<int Channel> 
void deduce_mask(Matrix const &src, int mask[]) 
{ 
    static_for<0, Channel>()([&](int i) 
    {    
     mask[mapper(0, 1, i)] = src(row - 1, col)[i]; 
     mask[mapper(1, 1, i)] = src(row, col)[i]; 
     mask[mapper(2, 1, i)] = src(row + 1, col)[i];  
    }); 
} 

的Visual C++ 2012与/ Ob1进行命令行开关编译此代码到该:

push  0 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
push  1 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
push  2 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
push  3 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
push  4 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
... 

如果无法使用lambda函数,需要编写一个仿函数。 Functor与lambda函数相比有一个优势 - 你可以指定一个调用约定(如果你不介意的话)。如果函子的operator()函数有__fastcall调用约定,那么在汇编代码中将看到mov edx, x而不是push x

+0

谢谢,这个答案是非常优雅的(至少对我来说) – StereoMatching

+5

但是,并不是所有这些'调用'比正常循环慢吗?为什么编译器不能优化它们? – Kapichu

2

如果您希望将索引放入模板中,lego的响应虽然优雅,但非常棒,无法编译。 std::get<i>(some_tuple)

如果要实现在未来的这个附加功能,下面的代码将工作,并应与乐高的解决方案向后兼容(除了我用一个静态的应用,而不是运营商()方法):

template <int First, int Last> 
struct static_for 
{ 
    template <typename Lambda> 
    static inline constexpr void apply(Lambda const& f) 
    { 
     if (First < Last) 
     { 
      f(std::integral_constant<int, First>{}); 
      static_for<First + 1, Last>::apply(f); 
     } 
    } 
}; 
template <int N> 
struct static_for<N, N> 
{ 
    template <typename Lambda> 
    static inline constexpr void apply(Lambda const& f) {} 
}; 

现在,你可以做到以下几点:

static_for<0, Channel>::apply([&](auto i) // Changed from '(int i)'. In general, 'auto' will be a better choice for metaprogramming! 
{    
    // code... 
    mask[mapper(0, 1, i)] = src(row - 1, col)[i]; // Notice that this does not change 
    std::get<i.value>(some_tuple); // But here you must get the member .value 
    // more code... 
}); 

在VC++测试2015年我没有研究为什么这个工作,但我只能假设,std::integral_constant<T,...>定义隐式转换为T使用VA但是编译器无法确定隐式强制转换会生成constexpr,因此您必须使用i.value(它是constexpr)来检索该值。

寻址@汤姆的问题在评论 如果你想遍历一个参数包,你可以做以下的(同一实现):

template<typename... Args> 
inline constexpr auto foo(const Args&... args) 
{ 
    static_for<0,sizeof...(Args)>::apply([&](auto N) 
    { 
     std::cout << std::get<N.value>(std::make_tuple(args...)); 
    }); 
} 

foo(1,"a",2.5); // This does exactly what you think it would do 

如果std::get<N.value>(std::make_tuple(args...))长相丑陋,你可以创建另一个constexpr函数可以最小化代码。

+0

我认为你需要将'(int i)'改成'(auto i)',因为int :: value不好形成 – Caleth

+0

@Caleth好的!从多个来源复制/粘贴的结果。 – AOK

+0

令人惊叹!这使我的代码更具可读性。 – tom

2

With if constexpr我们可以改进AOK的解决方案。

template <int First, int Last, typename Lambda> 
inline void static_for(Lambda const& f) 
{ 
    if constexpr (First < Last) 
     { 
     f(std::integral_constant<int, First>{}); 
     static_for<First + 1, Last>(f); 
     } 
} 

有了这一点,我们可以摆脱那个::apply

static_for<0, Channel>([&](auto i) 
{    
    // code... 
    mask[mapper(0, 1, i)] = src(row - 1, col)[i]; // Notice that this does not change 
    std::get<i.value>(some_tuple); // But here you must get the member .value 
    // more code... 
}); 

不幸的是你还是得写i.value


请注意,这将不无if constexpr是可能的,因为AOK的方式将需要static_for局部模板特殊化。

相关问题