C ++模板函数优化失败

时间:2014-11-26 11:42:35

标签: c++ templates optimization

根据我的理解,在编译时评估为假的分支不会被采用。在下面的代码中,编译器是否应该丢弃包含导致错误的行的块?

#include <iostream>

template<int i1, int i2>
void TemplateFunc()
{
    std::cout << i1 << std::endl;
    if(i2 > 0)
    {
        std::cout << i2 << std::endl;
        // The following causes an error
        // "cannot allocate an array of constant size 0"
        int someThing[i2] = {276};
        std::cout << someThing[i2/2] << std::endl;
    }
}

int main(int argc, char** argv)
{
    TemplateFunc<1,2>();
    TemplateFunc<3,0>();
    return 0;
}

我已经尝试过VS 2012,g ++(在coliru上用&#34; g ++ -std = c ++ 11 -O3 -Wall -pedantic -pthread main.cpp&amp;&amp; ./a。 out&#34;)并使用nvcc(在cuda内核中使用类似的代码)。

2 个答案:

答案 0 :(得分:3)

我认为你误解了编译器的工作方式。在实例化模板时可以自由地丢弃检查,但是如果编译器要实例化模板,则必须首先进行编译。由于它没有编译,并且没有可供使用的替代模板,因此会因编译错误而失败。

如果这是一个模板你可以通过部分专业化来实现这一目标:

#include <iostream>

template<int i1, int i2>
class TemplateFunc {
public:
  void operator()() {
    ...code with an if...
  }
};

template<int i1>
class TemplateFunc<i1, 0> {
public:
  void operator()() {
    ...code without and if...
  }
};

int main(int argc, char** argv)  
{
  TemplateFunc<1,2>()();
  TemplateFunc<3,0>()();
  return 0;
}

在此代码中,由于特化,编译器将不会选择带有if的模板,因此不会发生编译错误。但是对于功能来说,今天的标准根本不允许使用这种方法。

答案 1 :(得分:2)

模板最终会让人感到困惑。举一个更简单的例子。

int main() {
  if (0) {
    int array[0];
  }
}

这会导致编译时诊断(在符合要求的实现上)。按照你的逻辑,它不应该,因为永远不会采用if块。

理论上,检查0是否为零是在运行时进行的,因此编译器必须考虑分支可能的可能性。

在实践中,编译器确实优化了常数条件,但这是在as-if规则下,它有效地说明优化不能改变程序的含义。未经优化的程序将有一个错误,将在编译时诊断出来。因此,优化的程序仍然存在在编译时诊断出来的错误。

有一些方法可以包含必须在编译时执行的检查。一种方法是模板部分特化,并从i2不是正面时将采取的专业化中删除违规代码:

template<int i1, int i2, bool i2_is_positive>
struct TemplateFuncImpl;

template<int i1, int i2>
struct TemplateFuncImpl<i1, i2, false> {
  static void Impl() {
    std::cout << i1 << std::endl;
  }
};

template<int i1, int i2>
struct TemplateFuncImpl<i1, i2, true> {
  static void Impl() {
    std::cout << i1 << std::endl;
    std::cout << i2 << std::endl;
    int someThing[i2] = {276};
    std::cout << someThing[i2/2] << std::endl;
  }
};

template<int i1, int i2>
void TemplateFunc()
{
    TemplateFuncImpl<i1, i2, (i2 > 0)>::Impl();
}

这无疑是非常冗长的,所以它可能不是你真正想要使用的东西。不幸的是,没有简单的方法。 @Columbo提到有一些if语句变体的提议,强制在编译时运行,但没有任何实际上使它成为标准。