C++ Primer (5th ed.) : Is “16.3 Overloading and Templates” wrong in all its “more specialized” examples?

前端 未结 2 443
孤城傲影
孤城傲影 2021-01-15 18:49

Section 16.3 of C++ Primer (5th edition) - Overloading and Templates -, teaches the function matching procedure in the presence of candidate function template(s) instantiati

相关标签:
2条回答
  • 2021-01-15 19:31

    You're given these declarations:

    using std::string;
    template <class T> string debug_rep(const T &); /* 1 */
    template <class T> string debug_rep(T *);       /* 2 */
    

    In the invocation

    string s("SO");
    debug_rep(&s);
    

    the &s produces a string*, which can only match the T const& of (1) when T is string*. For the T* in (2), there is a match for T bound to string. So, provided your quoting is correct, the book is wrong about

    debug_rep(const string *&)
    

    being a possible instantiation: there is no such.

    The instantiation resulting from T = string* would instead be

    debug_rep( string* const& )
    

    But which instantiation will be called?

    As a general rule the simplest match is superior, but I never manage to remember the exact rules, so, I ask Visual C++ (because its typeid(T).name() produces readable type names by default):

    #include <iostream>
    #include <string>
    #include <typeinfo>
    using namespace std;
    
    template< class T >
    struct Type {};
    
    template <class T> auto debug_rep( T const& )   // 1
        -> string
    { return string() + "1 --> T = " + typeid(Type<T>).name(); }
    
    template <class T> auto debug_rep( T* )         // 2
        -> string
    { return string() + "2 --> T = " + typeid(Type<T>).name(); }
    
    auto main() -> int
    {
        string s( "SO" );
        cout << debug_rep( &s ) << endl;
        cout << "The type 'int const' is shown as " << typeid(Type<int const>).name() << endl;
    }
    

    And it says:

    2 --> T = struct Type<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >
    The type 'int const' is shown as struct Type<int const >
    

    And so on for your second and third examples: apparently the author got some mixup regarding const.

    0 讨论(0)
  • 2021-01-15 19:32

    EDIT: Thanks to Alf answer, and its elegant trick to conserve complete information about a type while using typeid, I was able to write a program that addresses most of my questions (changing from std::string to int for output readability.)
    The complete code can be edited and run rextester online IDE.

    Let's define a few classes and methods:

    template <class> class Type{}; // Allowing to get full type information with typeid
     
    template <class T> std::string typeStr()
    { return typeid(Type<T>).name(); }
    
    template <class T> void debug_rep(const T &a) /* 1 */
    {
        std::cout << "[1] T type is: "    << typeStr<T>()
                  << "\t| arg type is: " << typeStr<decltype(a)>() << std::endl;
    }
    
    template <class T> void force_1(const T &a)   /* 1 */
    {
        std::cout << "[forced 1] T type is: "    << typeStr<T>()
                  << "\t| arg type is: " << typeStr<decltype(a)>() << std::endl;
    }
    
    template <class T> void debug_rep(T *a)       /* 2 */
    {
        std::cout << "[2] T type is: "    << typeStr<T>()
                  << "\t| arg type is: " << typeStr<decltype(a)>() << std::endl;
    }
    

    example 1

    Running:

    std::cout << "---First example---" << std::endl;
    int i = 41;
    debug_rep(&i);
    force_1(&i);
    

    Displays:

    ---First example---
    [2] T type is: class Type<int>  | arg type is: class Type<int *>
    [forced 1] T type is: class Type<int *> | arg type is: class Type<int * const &>
    

    Q1: we can remark that, when we call force_1, instantiating a template corresponding to #1, the argument type is int * const &, so the book is not correct, and the instantiated candidate #1 would be

    1. debug_rep(int* const &)

    example 2

    Running:

    std::cout << "---Second example---" << std::endl;
    const int *ip = &i;
    debug_rep(ip);
    force_1(ip);
    

    Displays:

    ---Second example---
    [2] T type is: class Type<int const >   | arg type is: class Type<int const *>
    [forced 1] T type is: class Type<int const *>   | arg type is: class Type<int const * const &>
    

    Q2.1: Calling force_1, we remark that the argument type will be int const * const &, so the book is missing const qualification on the reference. The instantiated candidate will actually be:

    1. debug_rep(const int * const &)

    Q2.2 The second candidate being debug_rep(const int *), it is an exact match for ip (which is a pointer to constant integer). To check if the first candidate has a lower rank, let's write:

    void debug_rep_plain_b(const int * const &)   /* 1 */
    { std::cout << "[plain 1]" << std::endl;}
    
    void debug_rep_plain_b(const int *)           /* 2 */
    { std::cout << "[plain 2]" << std::endl; }
    

    If we try to compile:

    debug_rep_plain_b(ip)
    

    There is a compilation error for ambiguous call: So the answer to Q2.2 is NO, it is still an exact match ! For the templated version, the compiler actually uses the rule regarding the most specialized template to resolve the ambiguity.
    Even if there is a mistake in the deduced candidate, the book is correct regarding the fact that this example illustrates overload resolution using the most specialized template.

    example 3

    Running:

    std::cout << "---Third example---" << std::endl;
    const int ia[3] = {1, 2, 3};
    debug_rep(ia);
    force_1(ia);
    

    Displays:

    ---Third example---
    [2] T type is: class Type<int const >   | arg type is: class Type<int const *>
    [forced 1] T type is: class Type<int const [3]> | arg type is: class Type<int const (&)[3]>
    

    Q3.1 The type deduced for T by CL is array of const integer, so the book would be mistaken.

    BUT the result is inconsistent with GCC or Clang, that would output:

    ---Third example---
    [2] T type is:4TypeIKiE | arg type is: 4TypeIPKiE
    [forced 1] T type is:4TypeIA3_iE    | arg type is: 4TypeIRA3_KiE
    

    The interesting part being:
    [forced 1] T type is:4TypeIA3_iE
    meaning that they deduce T as an array of 3 non-const integers (because _i, not _Ki), which would agree with the book.

    I will have to open another question for this one, I cannot understand the type deduction operated by GCC and Clang...

    0 讨论(0)
提交回复
热议问题