1

I don't think I understand what the difference is between the 5 templated functions below, and hope that someone can explain. When should each one be used?

The first 2 has no default template parameters, but the last 2 do. The first and third ones set the default argument for comp to default_comparer<data_t>(), but the second and four ones do not. The last one seems useless in that the default is never used.

template<typename data_t>
struct default_comparer {
  bool operator()(const data_t& d1, const data_t& d2) const 
  {
    return d1 < d2;
  }
};

FIRST ONE

template<typename data_t, typename comparer_t>
pair partition(std::vector<data_t>& list, size_t pivot_idx, size_t start, size_t end, 
    const comparer_t& comp = default_comparer<data_t>()) 
{
  //do stuff
}

SECOND ONE

template<typename data_t, typename comparer_t>
pair partition(std::vector<data_t>& list, size_t pivot_idx, size_t start, size_t end, 
    const comparer_t& comp = comparer_t()) 
{
  //do stuff
}

THIRD ONE

template<typename data_t, typename comparer_t = default_comparer<data_t>>
pair partition(std::vector<data_t>& list, size_t pivot_idx, size_t start, size_t end, 
    const comparer_t& comp = default_comparer<data_t>()) 
{
  //do stuff
}

FOURTH ONE

template<typename data_t, typename comparer_t = default_comparer<data_t>>
pair partition(std::vector<data_t>& list, size_t pivot_idx, size_t start, size_t end, 
    const comparer_t& comp = comparer_t()) 
{
  //do stuff
}

FIFTH ONE

template<typename data_t, typename comparer_t = default_comparer<data_t>>
pair partition(std::vector<data_t>& list, size_t pivot_idx, size_t start, size_t end, 
    const comparer_t& comp) 
{
  //do stuff
}
24n8
  • 1,898
  • 1
  • 12
  • 25

1 Answers1

2

In most circumstances, the fourth version is the reasonable version.

For the first two versions, the default argument will not be used unless the user explicitly provides a template argument for comparer_t, because template argument deduction happens before default arguments are plugged in; in other words, default arguments are not considered for the purpose of template argument deduction.

The difference between the third and fourth versions becomes evident when the user explicitly provides the comparer_t template argument. In this case, the default arguments of the these versions differ — the third version requires a conversion from default_comparer<data_t>, possibly resulting in an error.

The difference between the fifth version and the hypothetical version in which neither default template arguments nor default arguments are given is that the former allows the provided default template argument to be used as a fallback in case deduction fails for it (for example, when the user provides {} as the argument to comp and does not provide a template argument for comparer_t).

Incidentally, please note that the term comparator is more commonly used than comparer.

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • Thanks. In modern C++, when would you ever want to explicitly provide template arguments instead of having the template argument automatically deduced? – 24n8 Feb 06 '20 at 14:44
  • 1
    @Iamanon In this case not, but there are cases in which you want to pass the argument by hand. In fact, template arguments are just another form of arguments (along with function arguments). For example: `std::get<2>(tup)`, where `tup` is a `std::tuple`. The `2` argument will determine the return type, so it has to be passed as compile-time information; passing it as a template argument suffices in this case. – L. F. Feb 06 '20 at 15:20
  • Ah interesting. In this particular case why is the position of the tuple designed to be a template argument instead of a function argument like `std::get<>(tup,0)` instead of `std::get<0>(tup)` – 24n8 Feb 06 '20 at 15:54
  • @Iamanon The problem is that if `0` is a normal function argument, it cannot used in a compile-time expression to determine the return type. With a template argument `I`, the return type can be specified as `std::tuple_element_t`. – L. F. Feb 07 '20 at 00:01