Templates and generic programming
T.65
Use tag dispatch to provide alternative implementations of a function
Reason
- A template defines a general interface.
- Tag dispatch allows us to select implementations based on specific properties of an argument type.
- Performance.
Example
This is a simplified version of std::copy (ignoring the possibility of non-contiguous sequences)
struct trivially_copyable_tag {};
struct non_trivially_copyable_tag {};
// T is not trivially copyable
template<class T> struct copy_trait { using tag = non_trivially_copyable_tag; };
// int is trivially copyable
template<> struct copy_trait<int> { using tag = trivially_copyable_tag; };
template<class Iter>
Out copy_helper(Iter first, Iter last, Iter out, trivially_copyable_tag)
{
// use memmove
}
template<class Iter>
Out copy_helper(Iter first, Iter last, Iter out, non_trivially_copyable_tag)
{
// use loop calling copy constructors
}
template<class Iter>
Out copy(Iter first, Iter last, Iter out)
{
using tag_type = typename copy_trait<std::iter_value_t<Iter>>::tag;
return copy_helper(first, last, out, tag_type{})
}
void use(vector<int>& vi, vector<int>& vi2, vector<string>& vs, vector<string>& vs2)
{
copy(vi.begin(), vi.end(), vi2.begin()); // uses memmove
copy(vs.begin(), vs.end(), vs2.begin()); // uses a loop calling copy constructors
}
This is a general and powerful technique for compile-time algorithm selection.
Note
With C++20 constraints, such alternatives can be distinguished directly:
template<class Iter>
requires std::is_trivially_copyable_v<std::iter_value_t<Iter>>
Out copy_helper(In, first, In last, Out out)
{
// use memmove
}
template<class Iter>
Out copy_helper(In, first, In last, Out out)
{
// use loop calling copy constructors
}
Enforcement
???