为什么指针衰减优先于推导的模板?

比方说,我正在写一个函数来打印一个string的长度:

template <size_t N> void foo(const char (&s)[N]) { std::cout << "array, size=" << N-1 << std::endl; } foo("hello") // prints array, size=5 

现在我想扩展foo来支持数组:

 void foo(const char* s) { std::cout << "raw, size=" << strlen(s) << std::endl; } 

但事实certificate,这打破了我原来的使用目的:

 foo("hello") // now prints raw, size=5 

为什么? 这不需要数组到指针的转换,而模板将是一个完全匹配? 有没有办法确保我的数组函数被调用?

这种(符合标准的)模糊性的根本原因似乎在于转换成本:重载解决方法试图最小化将参数转换为相应参数所执行的操作。 数组实际上是指向其第一个元素的指针,用一些编译时types信息来装饰。 数组到指针的转换并不比花费更多,比如保存数组本身的地址,或者初始化一个引用。 从这个angular度来看,模棱两可似乎是合理的,尽pipe在概念上它是不直观的(也可能是欠佳的)。 实际上,这个论证适用于所有左值转换,正如下面的引用所暗示的。 另一个例子:

 void g() {} void f(void(*)()) {} void f(void(&)()) {} int main() { f(g); // Ambiguous } 

以下是强制性的标准。 不是某些函数模板特化的函数优先于那些函数,如果两者都是同样好的匹配(参见[over.match.best] /(1.3),(1.6))。 在我们的例子中,执行的转换是一个数组到指针的转换,这是一个具有完全匹配的左值转换(根据[over.ics.user]中的表12)。 [over.ics.rank] / 3:

  • 标准转换序列S1是比标准转换序列S2更好的转换序列

    • S1S2的适当子序列(比较13.3.3.1.1定义的规范forms的转换序列, 排除任何左值变换 ;身份转换序列被认为是任何非同一性转换序列的子序列),或者if不是那个,

    • S1的级别优于S2的级别,或者S1S2具有相同的级别并且可以通过下面的段落中的规则来区分,或者如果不是这样,

    • [..]

第一个要点不包括我们的转换(因为它是一个左值转换)。 第二个需要排名的差异,这是不存在的,因为两个转换具有完全匹配等级; “下面的段落中的规则”,即[over.ics.rank] / 4,也不包括数组到指针的转换。
所以,不pipe你信不信,两个转换序列都不会比其他转换序列更好,因此char const*过载被挑选出来。


可能的解决方法:将第二个重载定义为函数模板,然后部分sorting并select第一个重载。

 template <typename T> auto foo(T s) -> std::enable_if_t<std::is_convertible<T, char const*>{}> { std::cout << "raw, size=" << std::strlen(s) << std::endl; } 

演示