function which is able to return different types?

后端 未结 4 1293
后悔当初
后悔当初 2020-12-31 08:36

I am trying to create a function in c++, I am wondering if I can create it such that it is able to return different types of vectors. e.g based on different case it returns

相关标签:
4条回答
  • 2020-12-31 09:20

    It depends on exactly what you're trying to accomplish, but there multiple possibilities for how to do this. Here are a few that come to mind:

    If one of a specific list of return types is decided inside the function:

    Since you edited your question, this seems to be what you want. You might try boost::variant:

    boost::variant<int, double, std::string> foo() {
        if (something) 
            //set type to int
        else if (something else)
            //set type to double
        else
            //set type to std::string
    }
    

    If the return type depends on a template argument:

    You can use SFINAE to manipulate overload resolution:

    template<typename T, typename = typename std::enable_if<std::is_integral<T>::value, T>::type>
    std::vector<int> foo() {...}
    
    template<typename T, typename = typename std::enable_if<std::is_floating_point<T>::value, T>::type>
    std::vector<std::string> foo() {...}
    

    If the return type can be anything:

    A boost::any would work well:

    boost::any foo() {...}
    

    If the return type is always derived from a specific class:

    Return a smart pointer to the base class:

    std::unique_ptr<Base> foo() {
        if (something)
            return std::unique_ptr<Base>{new Derived1};
        if (something else) 
            return std::unique_ptr<Base>{new Derived2};
    }
    
    0 讨论(0)
  • 2020-12-31 09:26

    C++17 Update

    If you known the type at compile time, you can use templates as illustrated in this answer.

    If the type is known at runtime only, with c++17 as an alternative to boost::variant we have the std::variant.

    Here is a working example:

    #include <iostream>
    #include <string>
    #include <type_traits>
    #include <variant>
    #include <vector>
    
    using variant_vector = std::variant<std::vector<int>, std::vector<std::string>>;
    
    auto get_vector(int i)
    {
        if (i < 0)
            return variant_vector(std::vector<int>(3, 1));
        else
            return variant_vector(std::vector<std::string>(3, "hello"));
    }
    
    int main()
    {
        auto visit_vec = [](const auto& vec) {
            using vec_type = typename std::remove_reference_t<decltype(vec)>::value_type;
            if constexpr (std::is_same_v<vec_type, int>)
                std::cout << "vector of int:" << std::endl;
            else if constexpr (std::is_same_v<vec_type, std::string>)
                std::cout << "vector of string:" << std::endl;
            for (const auto& x : vec)
                std::cout << x << std::endl;
        };
        
        std::visit(visit_vec, get_vector(-1));
        std::visit(visit_vec, get_vector(1));
    
        return 0;
    }
    

    See it live on Coliru.

    In the code above, the function get_vector returns a std::variant object that either holds a std::vector<int> or a std::vector<std::string>. The contents of the returned object are inspected using std::visit.

    0 讨论(0)
  • 2020-12-31 09:37

    Templates

    Try this:

    template <typename T>
    std::vector<T> func( /* arguments */ )
    {
        std::vector<T> v;
        // ... do some stuff to the vector ...
        return v;
    }
    

    You can call this function with different type in this way:

    std::vector<int> func<int>( args );
    std::vector<double> func<double>( args );
    

    Alternatives

    This is one way, if you know the types at compile-time. If you don't know the type at compile-time but at run-time only, then you have different choices:

    1. Use unions. I can only recommend this, if you have very simple C-struct-like types which are called PODs (plain old data) in the C++ standard.
    2. Use some type of variant. For example there is boost::variant from the Boost libraries or QVariant from the Qt library. They are a safe kind of unions on more general types. They also allow some conversions between different types. For example setting something to an integer value will make it possible to read the same value as floating point number.
    3. Use boost::any which can wrap any type but does not allow conversions between them.
    4. Use inheritance and polymorphism. For this case you need a common base class, say Base. Then you create an array of pointers to that base preferably with std::shared_ptrs. So the array type would be std::vector<std::shared_ptr<Base>>. The std::shared_ptr is better than built in pointers in this case because the manage your memory automagically by reference counting.
    5. Use a dynamic language that doesn't care about types and performance.
    0 讨论(0)
  • 2020-12-31 09:37

    You can use templates, if you know what type to return before you call the function. But you can't have a function, which internally decide to return some type.

    What you can do is to create a class which will be a container for returned data, fill object of this class with desired data and then return this object.

    typedef enum { VSTRING, VINT, V_WHATEVER ... } datatype;
    
    class MyReturnClass {
    
        datatype d;
    
        // now either
        vector<string> * vs;
        vector<int> * vi;
    
        // or
        void * vector;      
    
     }
    
     MyReturnClass * thisIsTheFunction () {
    
           MyReturnClass * return_me = new MyReturnClass();
    
           return_me->datatype = VSTRING;
           return_me->vs = new Vector<String>;
    
           return return_me;
    
     }
    
    0 讨论(0)
提交回复
热议问题