【问题标题】:Create a tuple from a vector of strings in c++从 C++ 中的字符串向量创建元组
【发布时间】:2018-06-26 16:09:09
【问题描述】:

我有一个字符串向量,每个字符串都是将 std::to_string 应用于某些基本数据类型(例如 char、int、double)的结果。我想要一个函数将其撤消为适当类型的元组。

我有一个简单的函数模板来反转 std::to_string:

template<typename T>
T from_string(std::string s)
{
}

template<>
int from_string<int>(std::string s)
{
    return std::stoi(s);
}

template<>
double from_string<double>(std::string s)
{
    return std::stod(s);
}

//... and more such specializations for the other basic types

我想要一个类似的函数:

template<typename... Ts>
std::tuple<Ts> undo(const std::vector<std::string>>& vec_of_str)
{
    // somehow call the appropriate specializations of from_string to the elements of vector_of_str and pack the results in a tuple. then return the tuple.     
}

函数的行为应该是这样的:

int main()
{
    auto ss = std::vector<std::string>>({"4", "0.5"});
    auto tuple1 = undo<int, double>(ss);
    std::tuple<int, double> tuple2(4, 0.5);

    // tuple1 and tuple2 should be identical. 
}

我想我必须对Ts中的参数进行“迭代”(或许正确的说法是“解包”),调用前面的函数,from_string对每一个,然后将from_string每次应用的结果打包成一个元组。我已经看到(并使用过)解包模板参数包的示例 - 它们通常是递归的(但不是以函数调用自身的通常方式),但我不知道如何做其余的事情。

【问题讨论】:

  • 您必须知道原始类型是什么。这听起来像是一个 XY 问题。
  • 您将不得不解析字符串以确定它所代表的数据类型(例如是int 还是floatdouble)。
  • 我编辑了代码以表示解释字符串所需的类型被指定为函数撤消的模板参数。这是否解决了您的担忧?
  • 遗憾的是,元组完全是编译时解析的结构,而向量是运行时的。不可能从任意长度的向量生成任意元组。另一方面,如果向量是从元组类​​型构造的,并且您仍然可以访问该类型,则可以将相同的值填充回该特定元组类型的实例中。

标签: c++ templates variadic-templates variadic-functions variadic


【解决方案1】:

一个例子:

#include <vector>
#include <string>
#include <tuple>
#include <cassert>

#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/trim.hpp>

template<class... Ts, size_t... Idxs>
std::tuple<Ts...>
parse(std::vector<std::string> const& values, std::index_sequence<Idxs...>) {
    return {boost::lexical_cast<Ts>(boost::algorithm::trim_copy(values[Idxs]))...};
}

template<class... Ts>
std::tuple<Ts...> undo(std::vector<std::string> const& values) {
    assert(sizeof...(Ts) == values.size());
    return parse<Ts...>(values, std::make_index_sequence<sizeof...(Ts)>{});
}

int main() {
    auto ss = std::vector<std::string>({"4", "0.5"});
    auto tuple1 = undo<int, double>(ss);
    std::tuple<int, double> tuple2(4, 0.5);
    std::cout << (tuple1 == tuple2) << '\n';
    assert(tuple1 == tuple2);
}

如果字符串值不包含前导和/或尾随空格,则可以删除对 boost::algorithm::trim_copy 的调用。它在那里是因为boost::lexical_cast 在空白处失败。


如果没有boost::lexical_cast,您将需要重新实现它,例如:

template<class T> T from_string(std::string const& s);
template<> int      from_string<int>(std::string const& s)    { return std::stoi(s); }
template<> double   from_string<double>(std::string const& s) { return std::stod(s); }
// And so on.

template<class... Ts, size_t... Idxs>
std::tuple<Ts...>
parse(std::vector<std::string> const& values, std::index_sequence<Idxs...>) {
    return {from_string<Ts>(values[Idxs])...};
}

【讨论】:

  • 谢谢马克西姆。我必须说我不太明白它是如何工作的,但是从主要功能来看,它完全符合我的要求。有没有什么办法可以在没有提升的情况下完成这项工作?
  • @sitiposit 为您添加了一个没有提升的示例。
  • 可以使用std::index_sequence&lt;Idxs...&gt;std::index_sequence_for&lt;Ts...&gt; 简化一点,并从return 中删除std::tuple&lt;Ts...&gt;
  • 哇。这个答案简洁、正确(我测试过),并且在我发帖后一个小时内就出现了。非常感谢。
  • @sitiposit - 一种可能,更通用一点,from_string():template &lt;typename T&gt; T from_string (std::string const &amp; str) { std::istringstream iss{str}; T ret; iss &gt;&gt; ret; return ret; }
【解决方案2】:

对于 C++11——如果您没有 C++14(Maxim 的解决方案需要),或者如果您想学习实现递归可变参数模板,则很有用:

#include <string>
#include <vector>
#include <tuple>
#include <cassert>

template <std::size_t N, typename T>
struct Undo
{
    static void f(T& tuple, const std::vector<std::string>& vec_of_str)
    {
        Undo<N - 1, T>::f(tuple, vec_of_str);
        std::get<N - 1>(tuple) = from_string<
            typename std::tuple_element<N - 1, T>::type
        >(vec_of_str[N - 1]);
    }
};

template <typename T>
struct Undo<0, T>
{
    static void f(T&, const std::vector<std::string>&)
    {
    }
};

template <typename... Ts>
std::tuple<Ts...> undo(const std::vector<std::string>& vec_of_str)
{
    assert(vec_of_str.size() == sizeof...(Ts));
    std::tuple<Ts...> ret;
    Undo<sizeof...(Ts), std::tuple<Ts...>>::f(ret, vec_of_str);
    return ret;
}

【讨论】:

  • from_string 来自哪里?
  • @MaximEgorushkin:来自问题。
  • 这个答案的行为完全符合我的要求(我测试过)。我承认我并不完全理解这个答案或之前的答案(也是正确的),但这只是指向我自己的无知。我将使用前一个,因为它更简洁。谢谢!
  • @sitiposit:不客气! :-) 如果您有 C++14,请务必使用 Maxim 的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-14
  • 1970-01-01
  • 2021-09-12
  • 2014-09-24
  • 1970-01-01
  • 2018-03-09
相关资源
最近更新 更多