如何从 std::array<T, N>::p ointer 成员/依赖类型推断数组大小?
How to deduce array size out of std::array<T, N>::pointer member/dependent type?
我的目标是为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
来写同样的问题。
根据C++11标准,命名空间std
中template<class T> array
的data()
成员声明如下
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
//1
编译器实例化std::array<char, 2>
。这使得typedef pointer
=char*
,并编译(如果曾经使用过)具有以下签名的假想成员pointer data()
:
char* data();
typedef被替换是因为typedef和别名是程序员的语法糖,而不是编译器。编译器知道这是char*
,所以它就在那里
//2
使用签名为char*(void)
的函数(作为第一个参数)调用模板。(同样,std::array<char,2>::pointer
不是它自己的类型,而是char*
)。因此,调用是void(char*, char const*)
,这就是编译器试图从中推导模板的内容。这个调用不显示任何关于数组大小的信息,甚至不知道指针最初来自数组这一事实。
//3
这里是
void(std::array<char, 2> &, char const *);
如果需要,编译器可以推断大小甚至字符类型。
您遇到的问题是,对于所有N
,std::array<char, N>::pointer
都是char*
,所以当您调用ys.data()
时,您将一个char*
传递给函数,并且有无限个N
都匹配。编译器会因为不明确而使其失败。我看了array
,看不出有任何方法可以创建您正在寻找的替代品。
但是,由于您正在编写C++,因此只需使用std::string
就可以解决问题,而不必尝试处理C字符串。
正如其他人所说,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
成员吗?也许是的。尽管这个变量在指针中很容易衰减,但它不会很有用。
- 如何从C++中的依赖类型中获得它所依赖的类型
- 将依赖名称显式标记为类型名和模板的奇怪之处
- 当基类是依赖类型时,这是一个缺陷吗
- 通过依赖类型使用非类型模板参数的单类型模板参数类模板的部分专用化
- 使用 'typename' 关键字将非类型视为依赖上下文中的类型
- 为什么依赖模板类型在部分专用化中不可推导?
- 为什么即使直到最后才定义实际类型,也可以将依赖名称视为完整
- 为什么不需要在 C++20 中的依赖类型之前指定"typename"?
- 如何使用类型专用化模板方法,该类型本身就是一个模板,其中只有返回类型依赖于模板类型
- 类型依赖于可变参数模板的类
- 类型依赖模板名称
- 模板类型依赖和继承
- C++中的循环类型依赖项死锁
- c++ 中带有容器迭代器的循环类型依赖关系(GCC 失败,而 MSVC 正常)
- C++使变量类型依赖于用户输入
- 使返回类型依赖于调用源
- 如何专用化基于类型依赖类型的C++模板化类函数
- 将 POD 联合双关到基本类型:依赖于实现或符合标准
- 使用类型依赖模板名的声明
- 非类型模板参数,其类型依赖于另一个参数