根据我的理解,在编译时评估为假的分支不会被采用。在下面的代码中,编译器是否应该丢弃包含导致错误的行的块?
#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内核中使用类似的代码)。
答案 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
语句变体的提议,将强制在编译时运行,但没有任何实际上使它成为标准。