c++templates

Tag dispatching example in C++


I started to play with the example available here and modified it to get the following code:

#include <iostream>

struct slow_tag {}; 
struct fast_tag {};

template <typename T>
struct traits
{
  typedef slow_tag tag;
};

template <>
struct traits<int>
{
  typedef fast_tag tag;
};

template <typename T>
void work_dispatch(const slow_tag)
{
  std::cout << "Slow function" << std::endl;
}

template <typename T>
void work_dispatch(const fast_tag)
{
  std::cout << "Fast function" << std::endl;
}

template <typename T>
void work_dispatch(const T)
{
  work_dispatch(typename traits<T>::tag());
}

int main()
{
  std::cout << "Starting my program" << std::endl;
  work_dispatch(3.0);

  work_dispatch(3);
}

Can anyone explain my why this particular (modified) example crashes with a segmentation fault? If I compile it I don't get any type of warning even when using -Wall with g++ 4.x.


Solution

  • I'll reduce your code to a simple example:

    #include <iostream>
    
    template <typename T>
    void work_dispatch(double)
    {
      std::cout << "Slow function" << std::endl;
    }
    
    int main()
    {
      work_dispatch(3.0);
    }
    

    Compile error:

    main.cpp:11:3: error: no matching function for call to 'work_dispatch'
      work_dispatch(3.0);
      ^~~~~~~~~~~~~
    main.cpp:4:6: note: candidate template ignored: couldn't infer template argument 'T'
    void work_dispatch(double)
         ^
    1 error generated.
    

    In other words you can't call this template

    template <typename T>
    void work_dispatch(double)
    {
      std::cout << "Slow function" << std::endl;
    }
    

    with

    work_dispatch(3.0);
    

    since there is no way you can deduce the type T, nor you're passing it explicitly. Therefore you have a stack overflow due to an infinite recursion:

    template <typename T>
    void work_dispatch(const T) <----------------|
    {                                            | This ends up calling itself
      work_dispatch(typename traits<T>::tag()); -|
    }
    

    To fix your code the easiest solution is to provide the type yourself

    template <typename T>
    void work_dispatch(const T)
    {
      work_dispatch<T>(typename traits<T>::tag());
    }
    

    Example