Reputation: 499
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
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
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());
}
Upvotes: 12
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
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:
T
reference)Upvotes: 0
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