partial specialization ordering with non-deduced context

前端 未结 3 515
轮回少年
轮回少年 2020-12-06 03:04

According to [temp.class.order] §14.5.5.2, the selection of a partial specialization of t in this example:

template< typename >
struct s {         


        
3条回答
  •  轻奢々
    轻奢々 (楼主)
    2020-12-06 03:35

    The information in this answer is based in large part off of this question. The template partial ordering algorithm is underspecified by the standard. The main compilers seem to at least agree on what the algorithm should be though.


    To start with, your two examples aren't equivalent. You have two template specializations in addition to your primary template, but with your function example, you're not adding a function overload for the primary. If you add it:

    template 
    constexpr int f( t ) { return 0; } 
    

    The function call becomes ambiguous as well. The reason for this is that partial ordering type synthesis algorithm does not instantiate templates and instead synthesizes new unique types.

    First, if we compare the function I just introduced to this one:

    template< typename c >
    constexpr int f( t< c, typename c::v > ) { return 1; }
    

    We have:

    +---+---------------------+----------------------+
    |   | Parameters          | Arguments            |
    +---+---------------------+----------------------+
    | 0 | c, typename c::v    | Unique0, void        |
    | 1 | c, void             | Unique1, Unique1_v   |
    +---+---------------------+----------------------+
    

    We ignore non-deduced contexts in the partial ordering deduction rules, so Unique0 matches c, but Unique1_v does not match void! Thus 0 is preferred. This is probably not what you expected.

    If we then compare 0 and 2:

    +---+--------------------------+----------------------+
    |   | Parameters               | Arguments            |
    +---+--------------------------+----------------------+
    | 0 | s, typename s::w   | Unique0, void        |
    | 2 | c, void                  | Unique2, Unique2_v   |
    +---+--------------------------+----------------------+
    

    Here, the 0 deduction fails (since Unique0 won't match s), but the 2 deduction also fails (since Unique2_v won't match void). That's why it's ambiguous.


    This lead me to an interesting question about void_t:

    template 
    using void_t = void;
    

    This function overload:

    template< typename c >
    constexpr int f( t< s< c >, void_t>> ) { return 3; }
    

    would be preferred over 0, since the arguments would be s and void. But this one would not be:

    template 
    struct make_void {
        using type = void;
    };
    
    template< typename c >
    constexpr int f( t< s< c >, typename make_void>::type> ) { return 4; }
    

    Since we wouldn't instantiate make_void> in order to determine ::type, so we end up in the same situation as 2.

提交回复
热议问题