【问题标题】:Constant character pointer using unique_ptr使用 unique_ptr 的常量字符指针
【发布时间】:2018-07-16 15:30:16
【问题描述】:

假设我有一个vector<string> input,我需要将它传递给一个接受const char** 参数的函数。我的想法是像这样使用unique_ptr

const auto output = make_unique<char*>(size(input));

但我似乎无法将const unique_ptr&lt;const*&gt; 变成const char**。有没有办法做到这一点,或者更简单的替代方案?

【问题讨论】:

  • @Ron Well...unique_ptr 是标准对象的名称,因此保留其大小写似乎是正确的...
  • const* 应该是什么?
  • 该函数是否采用char** 以便它可以更改char* 指向的内容,还是需要一个字符串数组?
  • @MilesBudnek 该函数位于 C-API 中。它正在对所有字符串进行深度复制。所以我们实际上是在谈论const char* const*,但出于这个问题的目的,我们可能应该像我不知道那样对待它,它实际上是在要求const char*
  • @JonathanMee 我非常确信您确实编写了数组过载,以至于我的大脑完全关闭了。

标签: c++ unique-ptr c-strings char-pointer const-char


【解决方案1】:

我只需构建一个指向字符串的c_str() 的指针向量,然后获取指向它的指针。

std:vector<const char*> pointers;
pointers.reserve(input.size());
for (const auto& e : input)
    pointers.push_back(e.c_str()); // get const char *'s
auto argument = pointers.data(); // get pointer to const char*'s - const char**

或者使用unique_ptr

auto pointers = std::make_unique<const char*[]>(size(input))
for (size_t i = 0; i < input.size(); ++i)
    pointers[i]= input[i].c_str(); // get const char *'s
auto argument = pointers.get(); // get pointer to const char*'s - const char**

【讨论】:

  • @JonathanMee 你可以做auto pointers = std::unique_ptr&lt;const char*[]&gt;(new const char*[input.size()]); 然后做for循环。
  • @JonathanMee 在这种情况下,没有充分的理由打扰std::unique_ptr,因为std::string 是存储拥有的字符串的正确表达方式,而std::unique_ptr 只是添加了额外的代码.
  • @NathanOliver 对,你是 const auto output = make_unique&lt;const char*[]&gt;(size(input)) 工作正常。
  • 我同意 Xirema。我已经编写了使用唯一指针执行此操作的代码,同时使旧代码异常安全,但在那之后我做的第一件事就是使用向量。
  • @JonathanMee Blast,我忘了make_unique 使用数组形式。我刚刚联系了std::vector,因为这是我通常使用的。我不想在答案中添加unique_ptr 版本吗?
【解决方案2】:

我假设你需要这个来适应一些你无法控制的界面,否则我会考虑调整有问题的界面以避免为了适应不合适的界面而不必要地创建临时数据......

由于您只需要一个已知大小的临时数组,最简单的解决方案可能是分配一个指针数组并用指向向量中字符串的指针填充它:

auto pointers = std::make_unique<const char*[]>(size(v));
std::transform(begin(v), end(v), &pointers[0], [](const auto& s) { return s.c_str(); });

这个数组也可以放在堆栈上以避免动态内存分配。但是由于您在这里使用字符串并且愿意将数据复制到临时数组中,所以我认为性能并不重要,所以我想没有必要增加复杂性……

【讨论】:

  • 我的意思是我认为transform(begin(v), end(v), pointers.get(), [](auto i){ return data(i); }) 会更简单,不是吗?但是,是的,这就是我一直在寻找的答案。
  • 无偿使用可变 lambda。我不喜欢它。
  • 确实,在这里使用std::transform 会好很多。在我写这篇文章的那一刻,我不知何故没有想到这一点。我会更新我的答案。
  • @TimSeguine 我的意思是它要么是 lambda,要么是 for-loop,就像在 NathanOliver's answer 中一样。我个人更喜欢 lambda。但这实际上只是一种风格选择。
  • @Jonathan 我对使用 lambda 没有任何问题。我在使用带有可变捕获的 lambda 时遇到问题,而无需任何捕获。我也更喜欢转换版本而不是 for 循环。
【解决方案3】:

两种方法,取决于c接口是否需要空终止:

#include <vector>
#include <string>
#include <algorithm>

auto make_c_interface_null_terminated(std::vector<std::string> const &input) -> std::vector<const char*>
{
    auto result = std::vector<const char*>(input.size() + 1);
    auto to_c_str = [](auto&& str) { return str.c_str(); };
    std::transform(begin(input), end(input), begin(result), to_c_str);
    // implied:  result[result.size() - 1] = nullptr
    return result;
}

auto make_c_interface(std::vector<std::string> const &input) -> std::vector<const char*>
{
    auto result = std::vector<const char*>(input.size());
    auto to_c_str = [](auto&& str) { return str.c_str(); };
    std::transform(begin(input), end(input), begin(result), to_c_str);
    return result;
}


extern "C" void c_interface_requires_null(const char** argv);
extern "C" void c_interface_sized(size_t size, const char** args);


void test(std::vector<std::string> const &input)
{
    auto output1 = make_c_interface_null_terminated(input);
    c_interface_requires_null(output1.data());

    auto output2 = make_c_interface(input);
    c_interface_sized(output1.size(), output1.data());
}

【讨论】:

  • auto&amp;&amp; 是参数 lambda 的正确选择吗?由于str.c_str() 的生命周期与拥有的字符串相关联(最好不要在此期间发生变异或破坏)。
  • @Eljay auto&amp;&amp; 始终是 lambda 参数的正确类型。它使用与模板函数参数类型推导相同的类型推导过程。在这种情况下,它将推断为 std::string const&amp;,因为 input 是对 const 的引用。
猜你喜欢
  • 2020-03-25
  • 2013-08-02
  • 2015-06-22
  • 1970-01-01
  • 2020-10-23
  • 2021-10-07
  • 2016-12-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多