4

Since the function template in the following code is a member of a class template, it can't be specialized without specializing the enclosing class.

But if the compiler's full optimizations are on (assume Visual Studio 2010), will the if-else-statement in the following code get optimized out? And if it does, wouldn't it mean that for all practical purposes this IS a function template specialization without any performance cost?

template<typename T>
struct Holder
{
    T   data;

    template<int Number>
    void saveReciprocalOf();
};

template<typename T>
template<int Number>
void Holder<T>::saveReciprocalOf()
{
    //Will this if-else-statement get completely optimized out
    if(Number == 0)     data = (T)0;
    else                data = (T)1 / Number;
}

//-----------------------------------
void main()
{
    Holder<float> holder;
    holder.saveReciprocalOf<2>();
    cout << holder.data << endl;
}
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
zeroes00
  • 531
  • 3
  • 16

3 Answers3

3

Chances are it will be optimized. But if you want to be sure you can use a compile-time if by using templates, e.g. Boost’s MPL if_ implementation.

Or you can use SFINAE (Boost.enable_if).

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
1

Usually the way to solve this kind of problem is through overloading

template<typename T>
struct Outer {
  template<int I>
  void inner() { 
    inner(int_<I>());
  }

private:
  template<int I>
  struct int_ { };

  void inner(int_<0>) {
    // for I == 0
  }

  template<int I>
  void inner(int_<I>) {
    // for others...
  }
};

This mimics explicit specializations quite well, and even works in cases where type check for the other paths would go mad (unlike the if solution)

// ...
  template<int I>
  void inner(int_<I>) {
    int tmp[I];
  }
// ...

That works because this path is only taken if I != 0. Now in your case, i first of all wonder why you don't just pass a normal function argument. You don't seem to need the compile time nature of I.

// works just fine too in your example
template<typename T>
void Holder<T>::saveReciprocalOf(int Number)
{
    // Will this if-else-statement get completely optimized out
    if(Number == 0)     data = (T)0;
    else                data = (T)1 / Number;
}

This also has good chances of being optimized if the compiler inlines the function call. Using the template non-type parameter where it's not strictly necessary only limits the abilities of that function not to work with runtime values.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • I figured that the compiler is more likely to optimize out the unnecessary conditional blocks when the checked variable is a compile time constant. But if the optimization is as likely to happen when using a regular function argument, then I guess there's no point in it. – zeroes00 Apr 11 '10 at 13:18
  • @Zeroes00 i recommend to first compile and then see the assembly if you really think it could affect performance. Here is an example of the impressive optimizations compilers do: http://stackoverflow.com/questions/2419650/c-c-macro-template-blackmagic-to-generate-unique-name/2419715#2419715 – Johannes Schaub - litb Apr 11 '10 at 13:24
0

Thanks. Since I want to be certain the conditional is optimized out (because it needs to called very often deep inside loops and I use a switch-case outside the loops to pick the correct path), I'll probably end up using enable_if_c something like in the following code:

using boost::enable_if_c;

template<typename T>
struct Dummy
{    
    template<int N>
    typename enable_if_c<N==2,bool>::type             isPrimary() {return true;}

    template<int N>
    typename enable_if_c<N==3,bool>::type             isPrimary() {return true;}

    template<int N>
    typename enable_if_c<N==5,bool>::type             isPrimary() {return true;}

    template<int N>
    typename enable_if_c<N!=2&&N!=3&&N!=5,bool>::type isPrimary() {return false;}
};

To me this seems a bit less cluttered than the suggestion by Johannes. Although the last (default) case can get quite ugly.

zeroes00
  • 531
  • 3
  • 16
  • I suppose it would be entirely possible to compute if a value is a prime with template metaprogramming (at least up to some values - yours works only up to 6 anyway), so that there is no runtime function call at all. – UncleBens Apr 11 '10 at 13:47
  • I don't actually need primary numbers or reciprocals, I just used them as examples for figuring out how to specialize function templates inside a class template. – zeroes00 Apr 11 '10 at 13:55
  • notice that `int_` is available via `boost` likewise. It's `boost::mpl::int_`. – Johannes Schaub - litb Apr 11 '10 at 14:43