0

I was reading Chapter 25 from C++ Templates - The Complete Guide - 2nd ed., where, given code more or less like this

#include <utility>

template<typename...>
struct Tuple;

template<typename H, typename ...T>
struct Tuple<H, T...> {
    H h;
    Tuple<T...> t;
    Tuple() {}
    template<typename HH, typename ...TT>
    Tuple(HH&& h, TT&& ...t)
        : h{std::forward<HH>(h)}
        , t{std::forward<TT>(t)...} {}
    template<typename HH, typename ...TT>
    Tuple(Tuple<HH, TT...> const& other)
        : h{other.h}
        , t{other.t} {}
};

template<>
struct Tuple<> {};

and a t defined like this

Tuple<int, double, std::string> const t;

the following definition fails

Tuple<long int, long double, std::string> t2{t};

Ok, good, I knew why that was happening and I started writing a margin note:

The reason is that t is not const, which means that template<typename HH, typename ...TT> Tuple(HH&&, TT&& ...) is a perfect match for the call, whereas template<typename HH, typename ...TT> Tuple(Tuple<HH, TT...> const&) requies that const is added to the argument to match the parameter.

And I've verified it: if I make t const, then the definition of t2 is fine an calls the correct ctor.

But then (assuming I don't add the const as per previous paragraph) I wondered "What type of match is the second, if the first is perfect?"

I was in double as to which of the several non-perfect matches that would be, so I moved forward to Appendix C, pages 682-683, Section C.2 to find out:

  1. Perfect match. The parameter has the type of the expression, or it has a type that is a reference to the type of the expression (possibly with added const and/or volatile qualifiers).
  2. Match with minor adjustments. This includes, for example, the dacy of an array variable to a pointer to its first element or the addition of const to match an argument of type int** to a parameter of type int const* const*

And now I'm a bit puzzled, because the ctor template<typename HH, typename ...TT> Tuple(Tuple<HH, TT...> const& other), with reference to the definition of t2, has a parameter with type a reference to the type of the expression initializing it, because of template type deduction, with just an added cosnt. So isn't this exactly what a perfect match is, based on the excerpt from the book? Or am I misreading the book? Or my own code?

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • 1
    Book isn't standard so some statements might not be exactly precise. In your code you have a non-const object, hence it may be modified. So the function (ctor) which allows its modification is a better match than the `const&`: you need to add `const` in one case but nothing in the other. I think the book is a little bit "greedy" in its first point—it tries to eat the second point as well. – ixSci May 01 '22 at 10:03
  • For a more precise discussion, see [Implicit conversions](https://en.cppreference.com/w/cpp/language/implicit_conversion) and [Ranking of implicit conversion sequences](https://en.cppreference.com/w/cpp/language/overload_resolution#Ranking_of_implicit_conversion_sequences). In particular, adding `const` is known as a qualification conversion. – Igor Tandetnik May 01 '22 at 14:15

0 Answers0