如何消除这个模板的歧义? [英] How to disambiguate this template?

查看:107
本文介绍了如何消除这个模板的歧义?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类,它使用一个大小作为模板参数(现场演示):

 模板< std :: size_t SIZE> A类
{
char b [SIZE];
}

它有多个构造函数用于不同的目的:

 使用const_buffer_t = const char(&)[SIZE]; 
using my_type = A< SIZE> ;;

A():b {} {} //(1)无params
A(const_buffer_t):b {} {} //(2)复制给定缓冲区的内容
A(const char * const):b {} {} //(3)复制与它们适合大小的项目
explicit A(const my_type&):b {} {} // (4)复制构造函数

//(5)复制与大小合适的项目
template< std :: size_t OTHER_SIZE>
A(const char(&)[OTHER_SIZE]):b {} {}

//(6)从另一个大小的复制构造函数A
//项目,因为它们适合大小
模板< std :: size_t OTHER_SIZE>
explicit A(const A< OTHER_SIZE>&):b {} {}

使用这组构造函数,这个指令没有问题:

  // CASE 1 
//调用构造函数3: A 5(const char * const)
//期望构造函数5:A 5(const char(&)[11])$ ​​b $ b A 5 a(0123456789);

// CASE 2
//如所期望的,调用构造函数1:A 5()
A 5 b();

// CASE 3
//如所期望的,调用构造函数4:A 5(const A 5&)
A 5 c(b);

//情况4
//如所期望的,调用构造函数6:A 5(const A 9&)
A 9 c(b);

但是当调用 A< 5>(five)在构造函数2,3,4和5之间有一个模糊的调用。



所以我的问题是:




  • 为什么构造函数3优先于 CASE 1 中的构造函数5?

  • 当对象 A 是由具有相同大小的模板参数的静态数组构造时,是否有一种消除构造函数2,3,4,5的方法?

    解决方案

    在重载解析过程中对转换序列进行排序时,数组到指针的转换被视为完全匹配(C ++ 11 13.3.3.1.1 / 1表12)。与你的直觉相反,这意味着(3)和(5)对于 A <5> a(0123456789); 。领带被打破,因为 Xeo在他的评论中说了 - 赞成非模板(3)。你可以通过将(3)转换为模板来欺骗编译器:

      template< typename = void> 
    A(const char * const):b {} {}

    只会导致建筑模糊性。没有简单的方法来消除 const char(&)[] const char * 重载:最好的解决方案可以改变(3)接受指针和长度:

      A(const char * const,std :: size_t) :b {} {
    std :: cout<< size:< SIZE<< ctor 3\\\
    ;
    }

    顺便说一下,我会注意到添加 构造函数 title =Live code>消除 A(five)情况



    编辑:但是,有一种合理的方式来消除 char * 从数组构造函数构造函数,通过引用接受指针参数:

      template< typename T ,
    typename = typename std :: enable_if<
    std :: is_same< typename std :: remove_cv< T> :: type,char> {}
    > :: type>
    A(T * const&):b {} {std :: cout< size:< SIZE<< ctor 3 \\\
    ; }

    [这个特殊招数的信用到 dyp ,可能Johannes Schaub或Yakk或我(我很确定它不是我) 。]



    此模板通过引用有效地锁定到实际的类型 - 在数组到指针之间转换可能发生 - 然后约束引用非指针类型。 / p>

    I have a class which takes a size as a template parameter (live demo):

    template <std::size_t SIZE> class A
    {
        char b[SIZE];
    }
    

    It have multiple constructors for different purposes:

    using const_buffer_t = const char (&)[SIZE];
    using my_type = A<SIZE>;
    
    A()                         : b{} {} // (1) no params
    A(const_buffer_t)           : b{} {} // (2) copy contents of given buffer
    A(const char * const)       : b{} {} // (3) copy as many items as they fit into the size
    explicit A(const my_type &) : b{} {} // (4) copy constructor
    
    // (5) copy as many items as they fit into the size
    template <std::size_t OTHER_SIZE>
    A(const char (&)[OTHER_SIZE]) : b{} {}
    
    // (6) copy constructor from another sized A
    // copy as many items as they fit into the size
    template <std::size_t OTHER_SIZE>
    explicit A(const A<OTHER_SIZE> &) : b{} {}
    

    With this set of constructors there's no problem with this instructions:

    // CASE 1
    // Calls constructor 3: A<5>(const char * const)
    // Expecting constructor 5: A<5>(const char (&)[11])
    A<5> a("0123456789");
    
    // CASE 2
    // As expected, calls constructor 1: A<5>()
    A<5> b();
    
    // CASE 3
    // As expected, calls constructor 4: A<5>(const A<5> &)
    A<5> c(b);
    
    // CASE 4
    // As expected, calls constructor 6: A<5>(const A<9> &)
    A<9> c(b);
    

    But when calling A<5>("five") there's an ambiguous call between the constructors 2, 3, 4 and 5.

    So my questions are:

    • Why the constructor 3 is preferred over the constructor 5 in the CASE 1?
    • Is there a way to disambiguate the constructors 2, 3, 4, 5 when the object A<SIZE> is constructed with a static array of the same size of the template parameter?

    Thanks for your attention.

    解决方案

    Array-to-pointer conversion is considered to be an exact match when ranking conversion sequences during overload resolution (C++11 13.3.3.1.1/1 Table 12). Contrary to your intuition, that means that (3) and (5) are equally good matches for A<5> a("0123456789");. The tie is broken - as Xeo says in his comment - in favor of the non-template (3). You may think to trick the compiler by turning (3) into a template as well:

    template <typename=void>
    A(const char * const) : b{} {}
    

    but doing so will only result in ambiguity of the construction. There's really no easy way to disambiguate const char (&)[] and const char* overloads: the best solution may be to change (3) to accept a pointer and length:

    A(const char * const, std::size_t) : b{} {
      std::cout << "size: " << SIZE << " ctor 3\n";
    }
    

    Just in passing, I'll note that adding a size_t argument to the const char* const constructor also disambiguates the A("five") case.

    EDIT: There is, however, one reasonable way to disambiguate the char* constructor from the array constructor, accept pointer arguments by reference:

    template <typename T,
      typename=typename std::enable_if<
        std::is_same<typename std::remove_cv<T>::type, char>{}
      >::type>
    A(T* const&) : b{} { std::cout << "size: " << SIZE << " ctor 3\n"; }
    

    [Credit for this particular trick goes to dyp, and possibly Johannes Schaub or Yakk or me (I'm pretty sure it wasn't me).]

    This template effectively latches onto the actual type by reference - before array-to-pointer conversion can occur - and then constrains away references to non-pointer types.

    这篇关于如何消除这个模板的歧义?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆