简体   繁体   English

如何从std :: array中推导出数组大小 <T, N> ::指针成员/依赖类型?

[英]How to deduce array size out of std::array<T, N>::pointer member/dependent type?

My goal here is to write safe replacement for strcpy for case when destination buffer size is known during compilation, and I would like for buffer size to be deduced, so user won't need to know it. 我的目标是在编译期间知道目标缓冲区大小的情况下为strcpy编写安全替换,并且我希望推导出缓冲区大小,因此用户不需要知道它。 For example: 例如:

char xs[2] = { 0 };
strcpy(xs, "abc"); // buffer overflow!
printf("[%s]\n", xs);

Output for this will (hopefully) be: 这个的输出(希望)是:

[abc]

For simple case, when C-style array is passed, it can be written without much fuss: 对于简单的情况,当传递C风格的数组时,可以毫不费力地编写它:

template<size_t N>
char * safe_strcpy(char (& dst)[N], const char * src) noexcept {
    std::snprintf(dst, N, "%s", src);
    return & dst[0];
}

Size of array is deduced, snprintf takes care of placing terminating null byte, voilà. 推导出数组的大小,snprintf负责放置终止空字节,voilà。

I can sort-of adapt it to std::array as well: 我也可以将它改编为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();
}

But this version is not really a drop-in replacement: 但是这个版本并不是真正的替代品:

std::array<char, 2> ys = {};
strcpy(ys.data(), "abc"); // overflow!
safe_strcpy(ys, "abc");   // ok, but I needed to remove .data()

I want following case to work ok: 我希望以下案例可以正常工作:

safe_strcpy(ys.data(), "abc"); // "a" should appear in buffer

dependent type of ys.data() is std::array<char, 2u>::pointer {aka char*} , so I think it should be possible to deduce array size out of this, but I can't figure out how :/ 依赖类型的ys.data()std::array<char, 2u>::pointer {aka char*} ,所以我认为应该可以从中推断出数组大小,但我无法弄清楚如何:/

When I try something like this: 当我尝试这样的事情时:

template<size_t N>
typename std::array<char, N>::pointer
safe_strcpy(typename std::array<char, N>::pointer & dst, const char * src) {
    // etc...
}

compilation fails with error: 编译失败并出现错误:

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’

I tried with gcc 5.1.1 and clang 3.5.0, error in both is essentially the same. 我尝试使用gcc 5.1.1和clang 3.5.0,两者中的错误基本相同。 Is it possible to deduce type out of dependent type in C++ at all? 是否可以在C ++中推断出依赖类型的类型?

[edit] to all you kind people saying, that I should use std::string - you're missing the point here. [编辑]所有你善良的人说,我应该使用std :: string - 你在这里错过了重点。 I could've written same question with any STL container and ::iterator instead of ::pointer . 我可以用任何STL容器和::iterator而不是::pointer编写相同的问题。

The data() member of the template<class T> array in namespace std is -according to the C++11 standard- declared as follows 命名空间std template<class T> arraydata()成员是 - 根据C ++ 11标准 - 声明如下

T * data() noexcept;
const T * data() const noexcept;

but not like this: 但不是这样的:

pointer data() noexcept;
const_pointer data() const noexcept;

Even if it had been declared using the typedef there is no difference. 即使使用typedef声明它也没有区别。 Consider your example code: 考虑您的示例代码:

std::array<char, 2> ys = {}; // 1
strcpy(ys.data(), "abc"); // 2
safe_strcpy(ys, "abc"); // 3

// 1 // 1

Compiler instantiates std::array<char, 2> . 编译器实例化std::array<char, 2> Which makes the typedef pointer = char* and compiles (if ever used) a imaginary member pointer data() with the following signature: 这使得typedef pointer = char*并编译(如果使用过pointer data()具有以下签名的虚构成员pointer data()

char* data();

The typedef is substituted because typedefs and alias names are syntactic sugar for the programmer - not the compiler. typedef被替换,因为typedef和别名是程序员的语法糖 - 而不是编译器。 The compiler knows that this is char* so there it is. 编译器知道这是char*所以就是这样。

// 2 // 2

You call the template using (as your first argument) a function that has the signature char*(void) . 您使用(作为第一个参数)使用签名char*(void)的函数调用模板。 (And again std::array<char,2>::pointer is not a type of its own but char* ). (再次std::array<char,2>::pointer不是它自己的类型,而是char* )。 Therefore, the call is void(char*, char const*) and this is what the compiler is trying to deduce the template from. 因此,调用是void(char*, char const*) ,这就是编译器试图从中推导出模板的内容。 And this call does not exhibit any information about the array size, nor does it even know about the fact that the pointer comes from an array in the first place. 并且此调用不会显示有关数组大小的任何信息,也不会知道指针首先来自数组的事实。

// 3 // 3

Here your call is 在这里你的电话是

void(std::array<char, 2> &, char const *);

and the compiler can deduce size and even character types if required. 如果需要,编译器可以推断出大小甚至字符类型。

The problem you're encountering is that std::array<char, N>::pointer is char* for all N , so when you call ys.data() you pass a char* to the function and there are infinite N that all match. 你遇到的问题是std::array<char, N>::pointer所有N char* ,所以当你调用ys.data()你将char*传递给函数,并且有无限的N一切都好。 This the compiler fails it as ambiguous. 编译器将其视为模糊不清。 I took a look at array and I can't see any way to create a drop-in replacement that you're looking for. 我看了一下array ,我看不出有什么方法可以创建你正在寻找的替代品。

But since you're writing C++ you can solve the problem just by using std::string instead of trying to deal with C-strings. 但是,由于你正在编写C ++,你可以通过使用std::string而不是尝试处理C std::string来解决问题。

As others said there is no pointer member in std::array that has the information about the size, specially pointer doesn't have that information. 正如其他人所说, std::array中没有指针成员有关于大小的信息,特别是pointer没有该信息。

A very non portable alternative is to use ._M_elems instead of .pointer in GNU C++. 非常不便携的替代方法是在GNU C ++中使用._M_elems而不是.pointer

int main(){
  std::array<double, 10> arr;
  assert( std::extent<decltype(arr._M_elems)>() == arr.size() );
}

Should std::array have a standard carray and carray_type member? std::array应该有标准的carraycarray_type成员吗? perhaps yes. 也许是的。 Although this variable will decay so easily in a pointer that it won't be very useful. 虽然这个变量会在指针中轻易衰减,但它不会非常有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM