我想要一个函数重载,其中一个用例通过指针传递非数组参数,而另一个用例通过引用传递 C 数组参数。
不幸的是,传递 C 数组参数会选择两个候选函数,因为 C 数组的指针衰减必须处于同一考虑级别,从而使情况变得不明确。
#include <iostream>
static void foo(int* p) {
std::cout << "int* -> " << *p << "\n";
}
static void foo(int (&a)[4]) {
std::cout << "int[4] -> "
<< a[0] << " "
<< a[1] << " "
<< a[2] << " "
<< a[3] << "\n";
}
int main() {
int array[4]{ 10, 20, 30, 40 };
foo(array); // error: call to 'foo' is ambiguous
foo(array + 3);
}
我目前正在使用此解决方法。 我对这个解决方法不满意。
static void foo(int (*a)[4]) { /*...*/ }
...将通过...调用
foo(&array);
将指针传递更改为指针传递引用的替代方法
void foo(int*& p)
将阻止通过 foo(array + 3)
调用它,因为这是一个右值。
是否有另一种方法(或模板魔法)可以消除这两种情况的歧义?
上下文最好是 C++17,但如果可以用 C++20 或更高版本表达一个不错的解决方案,那仍然会令人感兴趣。
(XY 问题?)原始上下文是将指针或数组作为类构造函数参数进行参数传递。 最初的上下文是尝试创建一个“胖指针”,就像 Google 的 Miracle Pointer 一样,它是常规原始指针的直接替代品,但包含跨度和偏移信息以及偏执检查(这确实会产生额外的运行时开销)。 我尝试将示例简化为突出和最小的重现情况。
更新(回复评论):最终,我也希望它也支持和区分
const int[4]
和 const int*
。
不幸的是,传递 C 数组参数会选择两个候选函数
所以,你应该人为地让非数组构造函数变得更糟。 @user17732522 的评论中已经提供了最简单的方法。 但如果由于某种原因这对您不起作用,这里有一个较旧的方法:引入用户定义的转换。
template <typename T>
struct RawPtr
{
[[gnu::always_inline]] constexpr RawPtr(T* ptr)
: m_ptr(ptr)
{
}
T* m_ptr;
};
template <typename T>
struct FatPtr
{
FatPtr(RawPtr<T> p);
template <size_t N>
FatPtr(T (&p)[N]);
};
template <typename T>
FatPtr(T*) -> FatPtr<T>;
int main()
{
int arr[5] = {};
const char carr[3] = {};
FatPtr t1(arr);
FatPtr t2(arr + 3);
FatPtr t3(carr);
FatPtr t4(carr + 1);
}