views:

105

answers:

3

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;
}
+3  A: 

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
+1  A: 

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
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
@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
A: 

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
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
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
@zeroes00, notice that `int_` is available via `boost` likewise. It's `boost::mpl::int_`.
Johannes Schaub - litb