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