如何消除这个模板的歧义?

时间:2014-04-25 10:53:41

标签: c++ templates c++11 disambiguation

我有一个类,它将大小作为模板参数(live demo):

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

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

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{} {}

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

// 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);

但是在调用A<5>("five")时,构造函数2,3,4和5之间存在模糊的调用。

所以我的问题是:

  • 为什么构造函数3优先于CASE 1
  • 中的构造函数5
  • 当使用与模板参数大小相同的静态数组构造对象A<SIZE>时,有没有办法消除构造函数2,3,4,5的歧义?

感谢您的关注。

2 个答案:

答案 0 :(得分:3)

在重载决策期间对转换序列进行排序时,数组到指针的转换被认为是完全匹配(C ++ 11 13.3.3.1.1 / 1表12)。与你的直觉相反,这意味着(3)和(5)对A<5> a("0123456789");同样是很好的匹配。领带被打破 - 如Xeo says in his comment - 支持非模板(3)。您可以考虑通过将(3)转换为模板来欺骗编译器:

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

但这样做only result in ambiguity of the construction。实际上没有简单的方法来消除const char (&)[]const char*重载的歧义:最好的解决方案可能是更改(3)以接受指针和长度:

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

顺便提一下,我会注意到size_t构造函数中添加const char* const参数disambiguates the A("five") case

编辑:但是,one reasonable way to disambiguate the char* constructor from the array constructor,通过引用接受指针参数:

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"; }

[这个特殊技巧归功于dyp,可能还有Johannes Schaub或Yakk或我(我很确定这不是我)。]

此模板通过引用有效地锁定实际类型 - 在进行数组到指针转换之前 - 然后约束对非指针类型的引用。

答案 1 :(得分:2)

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

    <强>答案: 由于overload resolution。非模板化类函数是一等公民,因此具有比模板化函数更高的重载决策等级。因此,构造函数3优于模板构造函数5.