在这种情况下,为什么模板实例化深度超过了限制?

时间:2018-08-10 21:31:05

标签: c++ templates template-specialization

我用C ++键入以下代码:

#include <iostream>
template <int n, int i = 0>
class show {
public:
    show () {
        if (i % 2) {
            std::cout << i << std::endl;
            show <n-1, i+1>();
        }else {
            show <n,i+1>();
        }
    }
};

template <int i>
class show <0, i> {};



int main()
{
    show <6>();
}

我认为它将写出6个不能被2整除的第一个数字。相反,我得到一个错误

  

严重错误:模板实例超过最大100个

如果最多实例化12个实例,为什么会出现此错误?

3 个答案:

答案 0 :(得分:5)

尝试

show () {
   if ( i % 2 )
        std::cout << i << std::endl;

   show<n-(i%2 ? 1 : 0), i+1>();
}

或者,如果可以使用C ++ 17,也可以使用

show () {
    if constexpr (i % 2) {
        std::cout << i << std::endl;
        show <n-1, i+1>();
    }else {
        show <n,i+1>();
    }
}

您的代码有问题

show () {
    if (i % 2) {
        std::cout << i << std::endl;
        show <n-1, i+1>();
    }else {
        show <n,i+1>();
    }
}

并不重要,如果i % 2是或否:showshow<n-1, i+1>show<n, i+1>都被实现了。因此,实现了许多不必要的show并达到了模板实例化的最大数量。

C ++ 17引入if constexpr正是为了避免此类问题。

在C ++ 17之前的版本中,您可以解决

show<n-(i%2 ? 1 : 0), i+1>();

答案 1 :(得分:1)

在功能show()中:

show () {
    if (i % 2) {
        std::cout << i << std::endl;
        show <n-1, i+1>();
    }else {
        show <n,i+1>();
    }
}

类模板showif子句的两个部分都实例化。

您需要切换到if constexpr来进行单个正确的实例化:

show () {
    if constexpr (i % 2) {
        std::cout << i << std::endl;
        show <n-1, i+1>();
    }else {
        show <n,i+1>();
    }
}

EXAMPLE

答案 2 :(得分:1)

要查看发生了什么,让我们尝试“内联”更简单的表达式show<1>()的实例化:

在第一个递归级别中,这将是我们得到的:

if (0 % 2) {
    std::cout << 0 << std::endl;
    show <0,1>(); // <-- this will instantiate to a no-op.
} else {
    show <1,1>();
}

这是第二个:

if (0 % 2) {
    std::cout << 0 << std::endl;
} else {
    show <1,1>(); // <-- now this will instantiate
}

第三:

if (0 % 2) {
    std::cout << 0 << std::endl;
} else {
    if (1 % 2) {
        std::cout << 1 << std::endl;
        show <0,1>(); // <-- this will instantiate to a no-op.
    } else {
        show <1,2>();
    }
}

第四:

if (0 % 2) {
    std::cout << 0 << std::endl;
} else {
    if (1 % 2) {
        std::cout << 1 << std::endl;
    } else {
        show <1,2>(); // <-- now this will instantiate
    }
}

第五:

if (0 % 2) {
    std::cout << 0 << std::endl;
} else {
    if (1 % 2) {
        std::cout << 1 << std::endl;
    } else {
        if (1 % 2) {
            std::cout << 2 << std::endl;
            show <0,2>(); // <-- this will again instantiate to a no-op...
        } else {
            show <1,3>(); // <-- ... and I'm sure you can see where this is going.
        }
    }
}

这例证了已经指出的其他答案:模板是代码生成器。这意味着,如果编译器到达模板,它将始终从该模板生成代码,而不管程序在到达模板之前具有任何逻辑。

这是if constexpr可以防止的。如果if constexpr处的表达式的值为假,编译器将忽略上述if constexpr块中的任何代码。

如果没有C ++ 17编译器,则可以使用SFINAE达到相同的结果。