问题
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