动机:
几乎是为了好玩,我正在尝试编写一个函数重载,它可以区分参数是固定大小的数组还是指针。
double const d[] = {1.,2.,3.};
double a;
double const* p = &a;
f(d); // call array version
f(p); // call pointer version
我发现这特别困难,因为众所周知的事实是数组迟早会衰减为指针。一个天真的方法是写
void f(double const* c){...}
template<size_t N> void f(double const(&a)[N]){...}
不幸的是,这不起作用。因为在最好的情况下,编译器会确定数组调用 f(d)
以上是模棱两可的。
部分解决方案:
我尝试了很多东西,我能得到的最接近的是以下具体代码。另请注意,在此示例代码中,我使用 char
而不是 double
,但最后很相似。
首先,我必须使用 SFINAE 在函数的指针版本中禁用转换(从数组 ref 到 ptr)。其次,我必须重载所有可能的数组大小(手动)。
[可编译代码]
#include<type_traits> // for enable_if (use boost enable_if in C++98)
#include<iostream>
template<class Char, typename = typename std::enable_if<std::is_same<Char, char>::value>::type>
void f(Char const* dptr){std::cout << "ptr" << std::endl;} // preferred it seems
void f(char const (&darr)[0] ){std::cout << "const arr" << std::endl;}
void f(char const (&darr)[1] ){std::cout << "const arr" << std::endl;}
void f(char const (&darr)[2] ){std::cout << "const arr" << std::endl;}
void f(char const (&darr)[3] ){std::cout << "const arr" << std::endl;}
void f(char const (&darr)[4] ){std::cout << "const arr" << std::endl;}
void f(char const (&darr)[5] ){std::cout << "const arr" << std::endl;}
void f(char const (&darr)[6] ){std::cout << "const arr" << std::endl;} // this is the one called in this particular example
// ad infinitum ...
int main(){
f("hello"); // print ptr, ok because this is the fixed size array
f(std::string("hello").c_str()); // print arr, ok because `c_str()` is a pointer
}
这可行,但问题是我必须为 N
的所有可能值重复该函数并使用 template<size_t N>
让我回到零平方,因为使用模板参数,两个调用回到了平等的地位。
换句话说,template<size_t N> void f(char const(&a)[N]){std::cout << "const arr" << std::endl;}
没用。
有什么方法可以概括第二个重载而不回退到模棱两可的调用?还是有其他方法?
也欢迎提供 C++ 或 C++1XYZ 答案。
两个细节:1)我使用了clang
对于上述实验,2) 实际 f
最终将成为 operator<<
,我想知道这对解决方案是否重要。
解决方案总结(基于其他人的以下)并适应具体类型char
的例子。两者似乎都依赖于制作 char const*
编译器的指针不太明显:
- 一个奇怪的(便携?),(来自@dyp的评论。)在指针版本中添加引用限定符:
template<class Char, typename = typename std::enable_if<std::is_same<Char, char>::value>::type>
void f(Char const* const& dptr){std::cout << "ptr" << std::endl;}
template<size_t N>
void f(char const (&darr)[N] ){std::cout << "const arr" << std::endl;}
- 一个优雅的(来自@user657267 的特殊情况)
template<class CharConstPtr, typename = typename std::enable_if<std::is_same<CharConstPtr, char const*>::value>::type>
void f(CharConstPtr dptr){std::cout << "ptr" << std::endl;}
template<size_t N>
void f(char const (&darr)[N] ){std::cout << "const arr" << std::endl;}
最佳答案
这似乎对我有用
#include <iostream>
template<typename T>
std::enable_if_t<std::is_pointer<T>::value>
foo(T) { std::cout << "pointer\n"; }
template<typename T, std::size_t sz>
void foo(T(&)[sz]) { std::cout << "array\n"; }
int main()
{
char const* c;
foo(c);
foo("hello");
}
奖励std::experimental::type_traits
:
using std::experimental::is_pointer_v;
std::enable_if_t<is_pointer_v<T>>
你的评论让我尝试了一些更简单的方法
template<typename T> void foo(T) { std::cout << "pointer\n"; }
template<typename T, unsigned sz> void foo(T(&)[sz]) { std::cout << "array\n"; }
当然,这里的问题是 foo
现在可以为任何类型调用,这取决于您希望参数检查的松懈程度。
另一种方法是(ab)使用右值引用
void foo(char const*&) { std::cout << "pointer\n"; }
void foo(char const*&&) { std::cout << "array\n"; }
显然这不是万无一失的。
关于c++ - 是否可以重载可以从指针告诉固定数组的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28182838/