Reputation: 10596
From my understanding, a branch that is evaluated at compile-time to be false won't be taken. In the following code, shouldn't the compiler discard the block containing the line that is causing the error?
#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;
}
I've tried this with VS 2012, g++ (on coliru with "g++ -std=c++11 -O3 -Wall -pedantic -pthread main.cpp && ./a.out") and using nvcc (with similar code in a cuda kernel).
Upvotes: 3
Views: 148
Reputation: 6145
I think you have misunderstood the way the compiler works. It might be free to discard the check when the template is instantiated, but if the compiler is going to instantiate the template it must compile in the first place. Since it doesn't compile, and there are no alternative templates to use, it will fail with a compilation error.
Had this been a template class you could have made this possible with an partial specialization:
#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;
}
In this code, the template with an if will not be selected by compiler because of the specialization, and thus no compilation error will occur. But for functions this method is simply not allowed by todays standards.
Upvotes: 3
Reputation:
Templates only end up confusing matters. Take a simpler example.
int main() {
if (0) {
int array[0];
}
}
This causes a compile-time diagnostic (on a conforming implementation). By your logic, it shouldn't, because the if
block is never taken.
In theory, the check whether 0
is zero takes place at run-time, so the compiler has to account for the possibility that the branch might be taken.
In practice, compilers do optimise away constant conditions, but that's under the as-if rule, which effectively states that optimisations must not change the meaning of the program. The unoptimised program would have an error that would be diagnosed at compile-time. Therefore, the optimised program still has an error that gets diagnosed at compile-time.
There are some ways to include checks that must be performed at compile-time. One method would be a template partial specialisation, and removing the offending code from the specialisation that would be taken if i2
is not positive:
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();
}
This is admittedly very verbose, so it may not be something you actually want to use. Unfortunately, there is no simple method. @Columbo mentions there have been proposals for a variant of the if
statement that would be forced to run at compile-time, but nothing that actually made it into the standard.
Upvotes: 2