可变参数模板中的分支

时间:2017-02-17 11:48:55

标签: c++ c++11 templates

我不明白为什么这不会被编译:

#include <iostream>

template<int I1,int ...Is>
int getProdSeq() {

    if(sizeof...(Is)==0)
        return I1;
    else
        return I1*getProdSeq<Is...>();
}

int main() {

    int i = getProdSeq<9,7,8>();
    std::cout<<i<<std::endl;
}

在对函数的最后一次递归调用中,...IS应为空,因此应该采用第一个if-branch。编译器也确认了这一点(...IS为空):

main.cpp: In instantiation of 'int getProdSeq() [with int I1 = 8; int ...Is = {}]':

但编译器也会发出以下错误:

main.cpp:5:5: note: template argument deduction/substitution failed: main.cpp:10:36: note: couldn't deduce template parameter 'I1'

如果函数确实是使用空参数包调用的,那么这个错误是有意义的,但是这不应该是这种情况,因为我用第一个if语句规避了这个问题。为什么编译器仍在检查else分支?

3 个答案:

答案 0 :(得分:2)

无法编译,因为:

  1. 该功能未声明为constexpr
  2. if语句未评估编译时间,因此编译器必须生成两个分支,从而导致编译器错误。这可以用C ++ 17 if contexpr来解决。
  3. 示例(使用gcc-7 -std=gnu++1z编译):

    template<int I1, int ...Is>
    constexpr int getProdSeq() {
        if constexpr(sizeof...(Is)==0)
            return I1;
        else
            return I1*getProdSeq<Is...>();
    }
    

答案 1 :(得分:1)

如果在实例化

 template<int I1,int ...Is>
 int getProdSeq() {

...Is参数包为空,然后是以下调用:

    getProdSeq<Is...>();

实例化getProdSeq<>,当然不存在。仅仅因为前面的if语句永远不会执行它,并不意味着它仍然必须是可编译的。模板扩展的结果必须是有效的C ++代码,而事实并非如此。仅仅因为if语句else clasuse永远不会执行,并不会改变else内的任何内容必须仍然是有效C ++代码的事实。

一种方法是使用两个模板:

#include <iostream>

template<int I1>
int getProdSeq() {

       return I1;
}

template<int I1,int I2, int ...Is>
int getProdSeq() {

        return I1*getProdSeq<I2, Is...>();
}

int main() {

    int i = getProdSeq<9,7,8>();
    std::cout<<i<<std::endl;
}

答案 2 :(得分:0)

  

如果函数确实是使用空参数包调用的,那么这个错误是有意义的,但是这不应该是这种情况,因为我用第一个if语句规避了这个问题。为什么编译器仍在检查else分支?

您的所有代码都会将编译器的所有阶段都传递到优化步骤。例如,对于clang / llvm,分支可能仅在转换为IR后才被消除。 因此,类型检查/推断/ ...在“坏代码”被丢弃之前的某处失败。

所以想象你写了

int x;
if (false) {
    x = '123';
} else {
    x = 0;
}

这仍然是错误的,即使if-branch永远不会执行。