[英]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.