【问题标题】:C++ Template Function Optimizations FailC++ 模板函数优化失败
【发布时间】:2014-11-26 20:58:25
【问题描述】:

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

#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 上使用“g++ -std=c++11 -O3 -Wall -pedantic -pthread main.cpp && ./a.out”)和使用 nvcc(使用cuda 内核中的类似代码)。

【问题讨论】:

    标签: c++ templates optimization


    【解决方案1】:

    我认为您误解了编译器的工作方式。实例化模板时可以随意放弃检查,但如果编译器要实例化模板,它必须首先编译。由于它无法编译,并且没有可供使用的替代模板,因此它将失败并出现编译错误。

    如果这是一个模板class,你可以通过部分特化来实现:

    #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的模板由于特化,不会被编译器选中,因此不会出现编译错误。但是对于函数来说,这种方法在今天的标准中根本是不允许的。

    【讨论】:

    • 一个模板函数可以委托给一个模板类的静态方法,该模板类可以被特化。我刚刚将其包含在我的答案中。 :) 顺便说一句,i2 也可能是负数,所以我没有专门针对 i2 == 0 案例。
    • @hvd 静态函数是否提供了 operator() 所没有的功能?是的,可以添加三个状态作为第三个模板参数来支持负值,但是 IMO 我们的解决方案都已经够用了。
    • TemplateFunc&lt;1,2&gt;() 不会调用您的operator()。它只是构造一个TemplateFunc&lt;1,2&gt; 对象。要打电话给你的operator(),你需要写TemplateFunc&lt;1,2&gt;()(),这是我想避免的。
    【解决方案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 声明的变体,强制在编译时运行,但实际上并没有将其纳入标准。

    【讨论】:

    • 我还会提到静态如果。 +1
    • @Columbo 有这种事吗?我知道有人提出了建议,但我不知道他们是否被接受。
    • 官方没有这样的东西,没有。尽管如此,它仍然被考虑并将构成 OP 似乎想要的东西。 (我只是说我会提到它,没什么重要的)
    • @Columbo 设法找出一种更有用的方法将其包含在我的答案中,我想。 :) 感谢您的建议。
    • 感谢您的付出,但正如您所指出的,这非常冗长,而且为了我正在编写的测试,我不想重组程序的各个元素。跨度>
    猜你喜欢
    • 2012-02-15
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 2015-02-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多