Turning a BOUNDED std::list<class> of parameters into a type std::tuple<class,class,class> tup<classObj1, classObj2,classObj2>

老子叫甜甜 提交于 2019-12-13 10:11:05

问题


I have a class

class C
{
public:
    C() {}
private:
    int timesTriggered_;

    std::map<std::string, std::tuple<std::string, int, int, int, int, int>> mapST;
    std::vector<std::string> sv;
};

and some objects of this type:

C c1;
C c2;
C c3;

I have a map indexed by strings of class C

std::map<std::string, std::list<C>> cMap;

I add some objects of class C into a map

cMap["1"].push_back(c1);
cMap["2"].push_back(c2);
cMap["3"].push_back(c3);

I need a function that when passed the std::list will return (for example with three elements) an

std::tuple<C&, C&, C&>(c1, c2, c3)

Even though this needs to return an std::tuple, the items of the tuple are always of the same type, C. This has to work no matter how many items in the list there are, although an upper bound of the number of C objects at compile time is fine. So in essence this turns a list (with an upper bound on the number of elements) into a variadic tuple.

Here is a function that returns what I need:

template <typename... Obs> std::tuple<Obs &...> BindObservers(Obs &... obs)
{
    return std::tuple<Obs &...>(obs...);
}

And it is called like this:

auto obs2 = BindObservers(c1, c2, c3);

The problem is that to use BindObservers I need to know the number of parameters being passed to it, the things between the commas. I wish there was a BindObservers that worked like this:

auto obs2 = BindObservers(std::list<C>);

And obs2 would look like (pseudo-code):

std::tuple<C,C,more Cs here to the length of std::list<C>>(one parameter for each item in std::list<C> seperated by commas)

So it is a kind of template meta-programming.

I need it to work with c++11 since I don't have a c++14 compiler.


回答1:


This code works correctly on clang and g++ (using C++11):

http://coliru.stacked-crooked.com/a/c8071ab447e10a31

Produces a std::tuple with Max_N elements of the same type, and fills it up with the first values of the given list. If less elements in list than Max_N, fills the last elements with sentinel values.

In practice, using std::array<C, N> may be more useful for something like this, if there is a need to pass from a list with a dynamic number of elements to one from a constant number of elements.

#include <tuple>
#include <type_traits>
#include <list>
#include <iostream>
#include <typeinfo>

template<std::size_t Max_N, typename List>
class list_to_tuple {
public:
    using value_type = typename List::value_type;
    using iterator_type = typename List::const_iterator;
    using tuple_type = decltype(std::tuple_cat(
        std::tuple<typename List::value_type>(),
        typename list_to_tuple<Max_N-1, List>::tuple_type()
    ));

    tuple_type operator()(const List& lst, const value_type& sentinel) {
        return convert(lst.begin(), lst.end(), sentinel);
    }

    tuple_type convert(iterator_type begin, iterator_type end, const value_type& sentinel) const {
        list_to_tuple<Max_N-1, List> remaining;
        if(begin != end) {
            auto current = std::make_tuple(*begin);
            ++begin;
            return std::tuple_cat(current, remaining.convert(begin, end, sentinel));
        } else {
            return std::tuple_cat(std::make_tuple(sentinel), remaining.convert(begin, end, sentinel));
        }
    }
};

template<typename List>
class list_to_tuple<0, List> {
public:
    using value_type = typename List::value_type;
    using iterator_type = typename List::const_iterator;
    using tuple_type = std::tuple<>;

    tuple_type convert(iterator_type begin, iterator_type end, const value_type& sentinel) const {
        return std::tuple<>();
    }
};

int main() {
    std::list<int> lst = {1, 2, 3};
    list_to_tuple<5, std::list<int>> to_tup;
    auto tup = to_tup(lst, 0);
    std::cout << std::get<0>(tup) << std::endl;
    std::cout << std::get<1>(tup) << std::endl;
    std::cout << std::get<2>(tup) << std::endl;
    std::cout << std::get<3>(tup) << std::endl;
    std::cout << std::get<4>(tup) << std::endl;
    std::cout << typeid(tup).name() << std::endl;
}



回答2:


A list has an undetermined number of elements at compile time.

A tuple has a known number of elements at compile time.

Therefore there is no way for the compiler to deduce the number of elements in the tuple (as it requires information about the elements of the list which is only known during run time).

Try rephrasing a new question with an example with some integers to show what you want to achieve.




回答3:


From your question, you want:

auto obs2 = BindObservers(std::list<C>);

And obs2 would look like (pseudo-code):

std::tuple<C,C,more Cs here to the length of std::list<C>>

(one parameter for each item in std::list seperated by commas)

The type that a call to the BindObservers name (possibly of a template function) returns must be fully specified by its template arguments. In this case, you pass no template arguments, but you do pass a std::list<C>. BindObservers can deduce some template arguments from the type of std::list<C>.

The types deduced depend only on the type std::list<C>.

This means that all calls to BindObservers with two different std::list<C> must return the same type.

As you want a list of 1 element to return a different type than a list of 2 elements, and two different types are not the same type, this cannot be done.

Now, you could always return a large std::tuple with "enough" room up to some limit size. You could also take a function object, and invoke it with a std::tuple of up to some fixed size (where every version is instantiated, but only the correct version is invoked).

But you didn't ask about those possibilities, so the answer is "no, your problem cannot be solved".



来源:https://stackoverflow.com/questions/28637654/turning-a-bounded-stdlistclass-of-parameters-into-a-type-stdtupleclass-cl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!