如何消除这个模板的歧义? [英] How to disambiguate this template?
问题描述
我有一个类,它使用一个大小作为模板参数(现场演示):
模板< 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 (&)[]
andconst 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 theconst char* const
constructor also disambiguates theA("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屋!
- Why the constructor 3 is preferred over the constructor 5 in the