我的目标是在编译期间知道目标缓冲区大小的情况下为strcpy
编写安全替换,并且我希望推导出缓冲区大小,因此用户不需要知道它。例如:
char xs[2] = { 0 };
strcpy(xs, "abc"); // buffer overflow!
printf("[%s]\n", xs);
这个的输出(希望)是:
[abc]
对于简单的情况,当传递C风格的数组时,可以毫不费力地编写它:
template<size_t N>
char * safe_strcpy(char (& dst)[N], const char * src) noexcept {
std::snprintf(dst, N, "%s", src);
return & dst[0];
}
推导出数组的大小,snprintf负责放置终止空字节,voilà。
我也可以将它改编为std :: array:
template<size_t N>
typename std::array<char, N>::pointer
safe_strcpy(std::array<char, N> & dst, const char * src) noexcept {
std::snprintf(dst.data(), N, "%s", src);
return dst.data();
}
但是这个版本并不是真正的替代品:
std::array<char, 2> ys = {};
strcpy(ys.data(), "abc"); // overflow!
safe_strcpy(ys, "abc"); // ok, but I needed to remove .data()
我希望以下案例正常工作:
safe_strcpy(ys.data(), "abc"); // "a" should appear in buffer
依赖类型ys.data()
是std::array<char, 2u>::pointer {aka char*}
,所以我认为应该可以从中推断出数组大小,但我无法弄清楚如何:/
当我尝试这样的事情时:
template<size_t N>
typename std::array<char, N>::pointer
safe_strcpy(typename std::array<char, N>::pointer & dst, const char * src) {
// etc...
}
编译因错误而失败:
error: no matching function for call to ‘safe_strcpy(std::array<char, 2u>::pointer, const char [4])’
safe_strcpy(ys.data(), "abc");
^
(...)
note: template argument deduction/substitution failed:
note: couldn't deduce template parameter ‘N’
我尝试使用gcc 5.1.1和clang 3.5.0,两者中的错误基本相同。是否有可能在C ++中推断出依赖类型的类型?
[编辑]
对你所有善良的人说,我应该使用std :: string - 你在这里错过了重点。我可以用任何STL容器和::iterator
而不是::pointer
编写相同的问题。
答案 0 :(得分:1)
命名空间IF .. ELSE
中data()
的{{1}}成员是 - 根据C ++ 11标准 - 声明如下
template<class T> array
但不是这样的:
std
即使使用typedef声明它也没有区别。考虑一下您的示例代码:
T * data() noexcept;
const T * data() const noexcept;
编译器实例化pointer data() noexcept;
const_pointer data() const noexcept;
。这使得typedef std::array<char, 2> ys = {}; // 1
strcpy(ys.data(), "abc"); // 2
safe_strcpy(ys, "abc"); // 3
= std::array<char, 2>
并编译(如果使用过)具有以下签名的虚构成员pointer
:
char*
typedef被替换,因为typedef和别名是程序员的语法糖 - 而不是编译器。编译器知道这是pointer data()
所以它就是。
使用(作为第一个参数)使用签名char* data();
的函数调用模板。 (而且char*
不是它自己的类型,而是char*(void)
)。因此,调用是std::array<char,2>::pointer
,这是编译器尝试从中推导出模板的内容。并且此调用不会显示有关数组大小的任何信息,也不会知道指针首先来自数组的事实。
您的电话是
char*
如果需要,编译器可以推断出大小甚至字符类型。
答案 1 :(得分:0)
您遇到的问题是所有std::array<char, N>::pointer
的char*
为N
,因此当您致电ys.data()
时,您会传递{ {1}}到函数,并且有无限char*
全部匹配。编译器将其视为模糊不清。我看了N
,我看不出有什么方法可以创建你正在寻找的替代品。
但是既然您正在编写C ++,那么只需使用array
而不是尝试处理C字符串就可以解决问题。
答案 2 :(得分:0)
正如其他人所说,std::array
中没有指针成员有关于大小的信息,特别是pointer
没有这些信息。
非常不便于的替代方法是在GNU C ++中使用._M_elems
而不是.pointer
。
int main(){
std::array<double, 10> arr;
assert( std::extent<decltype(arr._M_elems)>() == arr.size() );
}
std::array
应该有标准carray
和carray_type
成员吗?也许是的。虽然这个变量会在指针中轻易衰减,但它不会非常有用。