c++ - 如何从 std::array<T, N>::pointer 成员/依赖类型中推断出数组大小?

标签 c++ arrays c++11 c++14

我的目标是为 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 负责放置终止空字节,瞧。

我也可以让它适应 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 .

最佳答案

data() template<class T> array的成员在命名空间 std是 - 根据 C++11 标准 - 声明如下

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 *);

如果需要,编译器可以推断出大小甚至字符类型。

关于c++ - 如何从 std::array<T, N>::pointer 成员/依赖类型中推断出数组大小?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34725934/

相关文章:

java - C++ 到 java 数组的转换

PHP使用子数组值按字母顺序对数组进行排序

javascript - 简单的 Javascript 画廊

c++ - 在不绕过共享语义的情况下通过引用传输 vector<shared_ptr<T>>

循环不终止时的 C++ 输入验证

c++ - Div 不是除法是什么

c++ - vkCreateInstance 抛出段错误

javascript - 从 Firebase 响应嵌套的 JSON 对象获取键/值对

c++ - 为什么我可以使用 operator= 而不是 operator== 与 C++11 大括号初始化器?

c++ - boost::ref 没有发生匹配调用错误,但 std::ref 没有