[英]How do I convert vectors of various types to std::string?
我从haskell和python来到 C++,其中有将数据类型转换为字符串的内置方法。
例如,在Haskell 中有一个多态函数show
。
我有兴趣在 C++ 中创建一些可以做类似事情的模板函数。
例如,我们可以将vector<int>
转换为类似这样的字符串。
string toString(vector<int> v)
{
ostringstream o;
for (int elem: v)
o << elem << " ";
return o.str()
}
这会将int
的字符串表示全部放在一行上。 现在,如果我想以这种方式转换vector<vector<int> >
怎么办。
string toString(vector<vector<int> > v)
{
ostringstream o;
for (auto elem : v)
{
o << toString(elem) << "\n";
}
}
我的问题是:如果我想创建一个与vector<class A>
和vector<vector<class A>
一起使用的多态toString
怎么办? 我该怎么办?
我需要添加一些功能来将类型class A
转换为std::string
:我是否只为该类型提供至少一种toString
? 模板机制是否解决了所有这些问题?
或者已经有代码可以做到这一点?
目前没有直接的通用方法可以做到这一点,但您可以简单地构建自己的方法。 这是一个示例程序,它将模仿您所追求的行为。
#include <exception>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
template<typename T>
std::string toString(const std::vector<T>& vec) {
std::ostringstream stream;
for (auto& elem : vec) {
stream << elem << " ";
}
stream << '\n';
return stream.str();
}
template<typename T>
std::string toString(const std::vector<std::vector<T>>& vec) {
std::ostringstream stream;
for (auto& elem : vec) {
stream << toString(elem);
}
stream << '\n';
return stream.str();
}
int main() {
try {
std::vector<int> valuesA{ 1, 2, 3, 4 };
std::cout << toString(valuesA) << '\n';
std::vector<std::vector<float>> valuesB { {1.0f, 2.0f, 3.0f},
{4.0f, 5.0f, 6.0f},
{7.0f, 8.0f, 9.0f}
};
std::cout << toString(valuesB) << '\n';
} catch( const std::exception& e ) {
std::cerr << "Exception Thrown: " << e.what() << std::endl;
return EXIT_FAILURE;
} catch( ... ) {
std::cerr << __FUNCTION__ << " Caught Unknown Exception" << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
输出
1 2 3 4
1 2 3
4 5 6
7 8 9
上面的代码适用于vector<T>
和vector<vector<T>>
但它不适用于所有情况。 如果向量中有嵌套向量,函数声明将无法识别它。 此外,它不会识别其他容器,例如maps
、 sets
、 lists
、 queues
等......从这里你必须生成这个函数来接受所有不同类型的容器......
此时,您将开始看到代码重复和重复模式。 因此,与其将函数声明为:
template<T>
std::string toString(const std::vector<T>& vec) { /* ... */ }
你可以模板container
本身......
template<template<class> class Container, class Ty>
std::string toString(const Container<Ty>& container ) { /*... */ }
现在这将适用于大多数容器,但有些容器让它正常工作可能有点棘手,例如std::map
因为它可以从std::pair
获取值,或者它可以基于两个相应的类型它的声明及其使用大括号初始化的构造函数。 这是您可能必须为这个特定容器重载函数的地方,但总体思路仍然适用。
这不仅仅是使用templates
,它还使用templates
,其中的参数本身就是templates
,如果您不熟悉它们,它们的语法对于初学者来说可能有点令人生畏。 我相信你可以找到大量关于template
template
参数的研究......
编辑
作为旁注,您仍然必须小心传递到Container<Ty>
的type
。 对于简单的内置类型,如int
、 float
、 char
、 double
等,这是直接的......
但是,如果您有自己的用户定义class
或struct
怎么办...
class Foo {
private:
int bar;
float baz;
public:
Foo() : bar{0}, baz{0.0f} {}
Foo(int barIn, float bazIn) : bar{barIn}, baz{bazIn} {}
};
然后您或尝试使用您的代码的其他人决定执行以下操作:
std::vector<Foo> foos { Foo(1, 3.5f), Foo(2, 4.0f), Foo(3, 3.14159f) };
std::string report = toString(foos);
以上并不是那么简单,因为程序或函数不知道如何将Foo
转换为std::string
。 因此,确实需要考虑到谨慎和考虑。 这是您可能需要额外的辅助模板化函数来将用户定义的类或结构转换为std::string
,然后您必须为这些类型专门化您的toString()
函数并在其中使用转换辅助函数...
现在,随着C++
语言随着标准的每个版本和对各种编译器的改进而发展,事情确实趋于变得更加简化,据说这将很快成为一种普遍现象,一种常见的重复模式,最终可能会变得流线型。 C++
的未来前景乐观。 已经有一些工具可以帮助您构建自己的工具。 随着时间的推移,这些工具变得易于使用,甚至可以简化您的代码和生产时间。
如果我想创建一个与
vector<class A>
和vector<vector<class A>
一起使用的多态toString
怎么办? 我该怎么办?
是的,在c++17 中,通过if constexpr
功能和递归函数模板的组合(即,将toString
作为递归函数模板)是可能的。
在跳转到泛型函数模板之前,您的class A
需要实现operator<<
重载,以便std::ostringstream::operator<<
可以使用它。 例如,让我们考虑
struct A
{
char mChar;
// provide a overload for operator<< for the class!
friend std::ostream& operator<<(std::ostream& out, const A& obj) /* noexcept */ {
return out << obj.mChar;
}
};
现在toString
函数看起来像下面这样:
#include <type_traits> // std::is_floating_point_v, std::is_integral_v, std::is_same_v
// std::remove_const_t, std::remove_reference_t
template<typename Type>
inline static constexpr bool isAllowedType = std::is_floating_point_v<Type>
|| std::is_integral_v<Type>
|| std::is_same_v<A, Type>;
//^^^^^^^^^^^^^^^^^^^ --> struct A has been added to the
// allowed types(i.e types who has operator<< given)
template<typename Vector>
std::string toString(const Vector& vec) /* noexcept */
{
std::ostringstream stream;
// value type of the passed `std::vector<Type>`
using ValueType = std::remove_const_t<
std::remove_reference_t<decltype(*vec.cbegin())>
>;
// if it is allowed type do concatenation!
if constexpr (isAllowedType<ValueType>)
{
for (const ValueType& elem : vec)
stream << elem << " ";
stream << '\n';
return stream.str();
}
else
{
// otherwise do the recursive call to toString
// for each element of passed vec
std::string result;
for (const ValueType& innerVec : vec)
result += toString(innerVec);
return result; // return the concatenated string
}
}
现在您可以将toString
调用到std::vector<std::vector<A>>
以及std::vector<A> aObjs
,以及std::vector< /* primitive types */ >
。
我是否只为该类型提供至少一种
toString
? 模板机制是否解决了所有这些问题?
模板专业化也是另一种选择。 但是,如果您可以访问 C++17,我会建议采用上述方式,这将对您在问题中提供的所有类型进行排序。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.