doc07b5 doc07b5 - 4 months ago 7
C++ Question

'if' statements on C++ template arguments

The following code gives me a warning when using the Intel compiler icpc13.

#include <iostream>

template<int N>
class base
{
public:
double x[N];
};

template<int N>
class derived : public base<2*N>
{
public:
void print()
{
if (N==1)
{
std::cout << this->x[1] << std::endl;
}
else if (N==2)
{
std::cout << this->x[3] << std::endl;
}
}

};


int main(int argc, char* argv[])
{
derived<1> temp1;
derived<2> temp2;

temp1.print();
temp2.print();
}



Result: % icpc-13.1.163 main.cpp main.cpp(29): warning #175:

subscript out of range std::cout<x[3]<

during instantiation of "void derived::print() [with N=1]" at line
41


This is obviously not a danger since the if statement protects this line of code if the template argument is 1.

I know that I "should" do template specialization for such things, but there is some shared code in the real functions that make the if statements on template arguments really handy.

Question is…is this a "bad" thing to do, or is this incorrect compiler behavior? I don't get warnings with gcc, or xlc.

I chose the pragma solution. This ends up looking like this:

void print()
{
#ifdef __INTEL_COMPILER
#pragma warning push
#pragma warning disable 175
#endif

if (N==1)
{
std::cout<<this->x[1]<<std::endl;
}
else if (N==2)
{
std::cout << this->x[3] << std::endl;
}

#ifdef __INTEL_COMPILER
#pragma warning pop
#endif

}

};


From what I can tell, the push saves the warning flags before the disable, and the pop restores them.

Answer

Compilers do check code branches even they're inactive due to compile-time constants.

ICPC seems to be the only that checks the array bounds, but you could possibly run into other annoying warnings, like from Visual C++, which warns about constant conditional expressions when checking N (C4127 with warning level 4).

I definitely would take care of the warning, your mileage may vary:

  • disable warning with compiler option -wd175
  • disable warning at this specific point with vendor-dependent #pragma warning(disable: 175)
  • specialize the derived<>::print() method and factor out common code:
template<>
void derived<1>::print()
{
    std::cout << x[1] << std::endl;
    call_common();
}
template<>
void derived<2>::print()
{
    std::cout << x[3] << std::endl;
    call_common();
}
  • factor out the switch on N into its own specialized method derived<>::print_x():
template<>
void derived<1>::print_x()
{
    std::cout << x[1] << std::endl;
}
template<>
void derived<2>::print_x()
{
    std::cout << x[3] << std::endl;
}
template<int N>
void derived<N>::print()
{
    print_x();
    // ...
}