3
votes

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.

1
I'm curious if the same warning surfaces if you instead just use: std::cout<<this->x[2*N-1]<<std::endl; as our entire function body (and clang give no warning either, so you can add that to your list). - WhozCraig
Using if is fine, you need to check if array index is out of boundary or not. Obviously, if N=2, X[3] is out of boundary and behavior is undefined. - billz
@billz if N=2 in the derived class, it is 4 in the base class, where the array is declared. Its not out-of-bounds. That was the purpose of public base<2*N> - WhozCraig
@WhozCraig you are right. :) - billz
It's not a particularly bad thing to do, but I think it's a reasonable warning for the compiler to give. I'd suppress the warning and leave a comment explaining why it's safe. - Alan Stokes

1 Answers

1
votes

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();
    // ...
}