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

标签 c++ arrays overload-resolution

假设我正在编写一个函数来打印字符串的长度:

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 以支持 non-arrays:

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

但事实证明,这打破了我原来的预期用途:

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

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

最佳答案

这种(符合标准的)歧义的根本原因似乎在于转换成本:重载解析试图最小化将参数转换为相应参数所执行的操作。一个数组实际上是指向它的第一个元素的指针,并装饰有一些编译时类型信息。数组到指针的转换不会花费更多,例如保存数组本身的地址,或初始化对它的引用。从这个角度来看,这种模棱两可似乎是有道理的,尽管在概念上它是不直观的(并且可能低于标准)。事实上,这个论点适用于所有左值变换,正如下面的引用所暗示的那样。另一个例子:

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:

  • Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if

    • S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form defined by 13.3.3.1.1, excluding any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence) or, if not that,

    • the rank of S1 is better than the rank of S2, or S1 and S2 have the same rank and are distinguishable by the rules in the paragraph below, or, if not that,

    • [..]

第一个要点不包括我们的转换(因为它是左值转换)。第二个需要排名不同,但不存在,因为两种转换都具有精确匹配排名; “以下段落中的规则”,即在 [over.ics.rank]/4 中,也不包括数组到指针的转换。
不管你信不信,这两个转换序列中没有一个比另一个更好,因此选择了 char const*-overload。


可能的解决方法:将第二个重载也定义为函数模板,然后开始部分排序并选择第一个。

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

Demo .

关于c++ - 为什么指针衰减优先于推导的模板?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28243371/

相关文章:

ruby - Ruby 中不一致的隐式哈希创建?

intellisense - 你如何查看重载visual studio mac 2019

c++ - 运算符<< 重载解析 (C++)

c++ - 如何强制执行析构函数的调用顺序

c++ - 为什么我必须向 poco 的某些方法提供指针而不是 SharedPtr

arrays - 对元胞数组的每个元素执行算术运算的最快方法是什么?

javascript - 更改其属性后渲染数组项

c++ - 根据参数类型推断重载函数的函数类型

c++ - 无需注册 DLL 即可解决 C4772 错误的任何方法?

c++ - "main"循环的聊天服务器设计