简体   繁体   English

如何将各种类型的向量转换为 std::string?

[英]How do I convert vectors of various types to std::string?

I am coming to C++ from and where there are built-in ways of converting data types to strings.我从来到 C++,其中有将数据类型转换为字符串的内置方法。

For example, in Haskell there is the polymorphic function show .例如,在Haskell 中有一个多态函数show

I am interested in creating some template functions in C++ that would do something similar.我有兴趣在 C++ 中创建一些可以做类似事情的模板函数。

For instance, we could convert vector<int> to a string something like this.例如,我们可以将vector<int>转换为类似这样的字符串。

string toString(vector<int> v)
{
    ostringstream o;
    for (int elem: v)
        o << elem << " ";
    return o.str()
}

This puts a string representation of the int s all on a line.这会将int的字符串表示全部放在一行上。 Now, what if I wanted to convert a vector<vector<int> > in this way.现在,如果我想以这种方式转换vector<vector<int> >怎么办。

string toString(vector<vector<int> > v)
{
   ostringstream o;
   for (auto elem : v)
   {
      o << toString(elem) << "\n";
   }
}

My question is : what if I wanted to create a polymorphic toString that works with vector<class A> and vector<vector<class A> ?我的问题是:如果我想创建一个与vector<class A>vector<vector<class A>一起使用的多态toString怎么办? How would I go about this?我该怎么办?

I would need to add some functionality for converting type class A to a std::string : do I just provide at least one specialization of toString for that type?我需要添加一些功能来将类型class A转换为std::string :我是否只为该类型提供至少一种toString Does the template mechanism sort all this out?模板机制是否解决了所有这些问题?

Or is there code to do this already?或者已经有代码可以做到这一点?

There is no current direct generic way to do this but you can simply build your own.目前没有直接的通用方法可以做到这一点,但您可以简单地构建自己的方法。 Here's a sample program that will mimic the behavior that you are after.这是一个示例程序,它将模仿您所追求的行为。

#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;
}

Output输出

1 2 3 4

1 2 3
4 5 6
7 8 9

The above code will work for vector<T> and vector<vector<T>> but it will not work in every situation.上面的代码适用于vector<T>vector<vector<T>>但它不适用于所有情况。 If you have a nested vector within a vector, the function declaration will not recognize it.如果向量中有嵌套向量,函数声明将无法识别它。 Also, it will not recognize other containers such as maps , sets , lists , queues , etc... from here you would then have to generate this function to accept all the different types of containers...此外,它不会识别其他容器,例如mapssetslistsqueues等......从这里你必须生成这个函数来接受所有不同类型的容器......

At this point, you will begin to see code-duplication and repetitive patterns.此时,您将开始看到代码重复和重复模式。 So instead of declaring the function as:因此,与其将函数声明为:

template<T>
std::string toString(const std::vector<T>& vec) { /* ... */ }

You could template the container itself...你可以模板container本身......

template<template<class> class Container, class Ty>
std::string toString(const Container<Ty>& container ) { /*... */ }

Now this will work for most containers, but some containers it can be a bit tricky to get it to work properly such as std::map because it can take values from an std::pair , or it can take two corresponding types based on its declaration in conjunction with its constructors that use brace-initialization.现在这将适用于大多数容器,但有些容器让它正常工作可能有点棘手,例如std::map因为它可以从std::pair获取值,或者它可以基于两个相应的类型它的声明及其使用大括号初始化的构造函数。 This is where you might have to overload the function for this specific container, but the general idea still applies.这是您可能必须为这个特定容器重载函数的地方,但总体思路仍然适用。

This is more than just using templates it is also using templates where their arguments are templates themselves and if you are not familiar with them, their syntax can be a bit daunting for a beginner.这不仅仅是使用templates ,它还使用templates ,其中的参数本身就是templates ,如果您不熟悉它们,它们的语法对于初学者来说可能有点令人生畏。 I'm sure you can find plenty of research on template template parameters...我相信你可以找到大量关于template template参数的研究......


Edit编辑

As a side note, you still have to be careful with the type being passed into Container<Ty> .作为旁注,您仍然必须小心传递到Container<Ty>type For simple built-in types such as int , float , char , double , etc. this is straight forward...对于简单的内置类型,如intfloatchardouble等,这是直接的......

However, what if you have your own user-defined class or struct ...但是,如果您有自己的用户定义classstruct怎么办...

class Foo {
private:
    int bar;
    float baz;
public:
    Foo() : bar{0}, baz{0.0f} {}
    Foo(int barIn, float bazIn) : bar{barIn}, baz{bazIn} {}
};

Then you or someone else who is trying to use your code decides to do:然后您或尝试使用您的代码的其他人决定执行以下操作:

std::vector<Foo> foos { Foo(1, 3.5f), Foo(2, 4.0f), Foo(3, 3.14159f) };
std::string report = toString(foos);

The above isn't so trivial because the program or the functions will not know how to convert Foo to std::string .以上并不是那么简单,因为程序或函数不知道如何将Foo转换为std::string So care and consideration does need to be taken into account.因此,确实需要考虑到谨慎和考虑。 This is where you might need additional helper templated functions to convert user-defined classes or structures to an std::string , then you would have to specialize your toString() function for those types and use the conversion helper function within it...这是您可能需要额外的辅助模板化函数来将用户定义的类或结构转换为std::string ,然后您必须为这些类型专门化您的toString()函数并在其中使用转换辅助函数...


Now, as the C++ language evolves with each release of the standard and improvements to various compilers, things do tend to become more simplified, with that being said this will soon become a common occurrence and a common repetitive pattern that may eventually become streamlined.现在,随着C++语言随着标准的每个版本和对各种编译器的改进而发展,事情确实趋于变得更加简化,据说这将很快成为一种普遍现象,一种常见的重复模式,最终可能会变得流线型。 There is a positive outlook for the future of C++ . C++的未来前景乐观。 There are already tools out there to assist you in building your own.已经有一些工具可以帮助您构建自己的工具。 As time progresses these tools become easily accessible to use and can even simplify your code and production time.随着时间的推移,这些工具变得易于使用,甚至可以简化您的代码和生产时间。

What if I wanted to create a polymorphic toString that works with vector<class A> and vector<vector<class A> ?如果我想创建一个与vector<class A>vector<vector<class A>一起使用的多态toString怎么办? How would I go about this?我该怎么办?

Yes it is possible in , by the cobination of if constexpr feature and a recursive function template (ie making the toString as recursive function template).是的,,通过if constexpr功能和递归函数模板的组合(即,将toString作为递归函数模板)是可能的。

Before jumping into the generic function template, your class A needs to implement operator<< overload so-that std::ostringstream::operator<< can make use of it.在跳转到泛型函数模板之前,您的class A需要实现operator<<重载,以便std::ostringstream::operator<<可以使用它。 For instance, lets consider例如,让我们考虑

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;
   }
};

Now the toString function would look like something as follows:现在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
   }   
}

Now you can call the toString to the std::vector<std::vector<A>> as well as std::vector<A> aObjs , and to the std::vector< /* primitive types */ > too.现在您可以将toString调用到std::vector<std::vector<A>>以及std::vector<A> aObjs ,以及std::vector< /* primitive types */ >

( See Complete Demo Online Live ) 见完整演示在线直播


Do I just provide at least one specialization of toString for that type?我是否只为该类型提供至少一种toString Does the template mechanism sort all this out?模板机制是否解决了所有这些问题?

Template specialization is another option too.模板专业化也是另一种选择。 However, if you have access to C++17, I would suggest the above manner, which will sort all of the types you provided in the question.但是,如果您可以访问 C++17,我会建议采用上述方式,这将对您在问题中提供的所有类型进行排序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从LPCTSTR转换为std :: string? - How do I convert from LPCTSTR to std::string? 如何将std :: wstring转换为LSA_UNICODE_STRING - How do I convert a std::wstring to an LSA_UNICODE_STRING 如何将“指向const TCHAR的指针”转换为“std :: string”? - How do I convert a “pointer to const TCHAR” to a “std::string”? 如何将包含双精度的std :: string转换为双精度矢量? - How do I convert a std::string containing doubles to a vector of doubles? 如何将std :: string转换为boost :: gregorian :: date? - How do I convert a std::string to a boost::gregorian::date? 如何将 wchar_t* 转换为 std::string? - How do I convert wchar_t* to std::string? 如何在C ++中输入和输出各种文件类型 - How do I input and output various file types in c++ 我该如何解决? (错误:'operator==' 不匹配(操作数类型是 'std::string' {aka 'std::__cxx11::basic_string'} 和 'int') - How do I fix this? (error: no match for ‘operator==’ (operand types are ‘std::string’ {aka ‘std::__cxx11::basic_string’} and ‘int’) 如何将std :: string转换为构造函数中的几种类型中的任何一种? - How to convert std::string to any of several types in constructor? 我如何将这个 std::string 转换为 std::basic 字符串? - How would I get convert this std::string into a std::basic string?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM