隐式转换为 const 类型时选择了错误的重载

Wrong overload selected when implicity converting to a const type

考虑以下代码:

#include <iostream>

// General overload using a template
template <typename SomeType>
void some_func(const SomeType p) {
    std::cout << "Using the general function" << std::endl;
}

// Specific overload, accepting a 'const double*' type
void some_func(const double* p) {
    std::cout << "Using the function accepting a const double*" << std::endl;
}

int main() {
    // This one uses the specific overload, as expected
    const double *a = new double(1.1);
    some_func(a);
    delete a;
    
    // This one uses the general function rather than the second overload
    double *b = new double(1.1);
    some_func(b);
    delete b;
    
    return 0;
}

在这段代码中,some_func函数有两个重载。第一个是最通用的重载,使用模板来捕获几乎任何类型。第二个重载是一个特定的重载,接受一个 const double* 类型作为它的参数。

main函数中,我首先创建const double*类型的变量a。当向 some_func 提供 a 时,第二个重载是 selected。这符合预期。其次,我创建了一个 double* 类型的变量 b(所以没有 const)。当向 some_func 提供变量 b 时,它 select 是第一个重载。我本来希望它是 select 第二次重载,因为(我认为)它应该能够隐式地将类型 double* 转换为 const double*。为什么在这种情况下 select 是第一次重载而不是第二次重载?

为了完整性,这是程序的输出:

$ g++ main.cpp
$ ./a.out
Using the function accepting a const double*
Using the general function

限定词const是一种契约。这意味着函数将无法通过指针更改值

所以编译器有两个选择:

  1. 要么从模板中实例化一个函数,该函数将能够(可能)更改值。
  2. 使用更严格的函数将无法更改它。

有可以改变的参数,选择第一种情况是合乎逻辑的。

因此,遵循精确类型选择规则,同时考虑 CV 限定符非常合乎逻辑。

在模板实例化期间,此 const SomeType p 会变成 double* const p,这比 const double* 更匹配。也就是说,模板的参数将成为指向可变数据的常量指针。
在模板函数中,实际上可以通过那个指针修改数据,所以可以假设它是一个更好的匹配。
如果将 const SomeType p 更改为 const SomeType* p,将选择特定的重载。