ad3angel1s
ad3angel1s

Reputation: 499

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.

Upvotes: 3

Views: 12483

Answers (5)

Phani Srikar
Phani Srikar

Reputation: 11

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

If the function has T used in the function, the compiler can auto deduce the type since it's not being used in any way it has no way to auto do it and we need to manually pass it.

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

In this case the compiler has enough info infer the type from the arguments passed during the function call, this code will run fine.

Upvotes: 0

Marco A.
Marco A.

Reputation: 43662

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

Upvotes: 12

Jarod42
Jarod42

Reputation: 217398

With signature

template <typename T>
void work_dispatch(const slow_tag);

T cannot be deduced, so you have to provide it in the call

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

As currently

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

call itself recursively until the crash.

Upvotes: 1

syntagma
syntagma

Reputation: 24324

Running you program under Valgrind shows that you have a stack overflow caused by this line:

  work_dispatch(3.0);

which executes this line: work_dispatch(typename traits<T>::tag()); over and over again (causing stack overflow).

EDIT: I think what you are trying to do here should be possible with the following fixes:

#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 T& val, const slow_tag& st) {
    std::cout << "Slow function" << std::endl;
}

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

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

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

That is, you should:

  1. Do the dispatch providing value (I have also changed the function argument to T reference)
  2. Pass the value to both templated function launched from the dispatch function.

Upvotes: 0

Bo Persson
Bo Persson

Reputation: 92271

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

The compiler cannot figure out what T should be in this function, so it is not considered in the overload resolution.

And you don't get any error, because "Substitution Failure Is Not An Error".

Upvotes: 0

Related Questions