У меня есть несколько классов-реализаторов (impls) и несколько оболочек для пользователя, реализованных на C ++. Я хочу держать impls и обертки в двух разных кортежах, чтобы я мог инициализировать свои impls одним выделением. (У меня есть и другие причины :).
Дело в том, что класс кортежей стандартной библиотеки visual studio 2012 не позволяет мне создавать свой кортеж-обертку без конструктора копий оберток с ссылками на const. к сожалению, мне нужно const_cast в этом случае, например:
#include <iostream>
#include <type_traits>
#include <tuple>
#include <typeinfo>
template <typename Member>
struct A
{
A(Member& m) : member(m)
{ std::cout << typeid(Member).name() << " MMBR " << member << std::endl; }
A(const Member& m) : member(const_cast<Member&>(m))
{ std::cout << typeid(Member).name() << " CMBR " << member << std::endl; }
void Print()
{
std::cout << typeid(Member).name() << " PRNT " << member << std::endl;
}
Member& member;//yes I need to hold this as a mutable reference
};
int main()
{
typedef std::tuple<A<int>, A<double>, A<short>> WrapperTuple;
typedef std::tuple<int, double, short> Tuple;
Tuple t(0, 1, 2);
WrapperTuple w(t);
std::get<1>(w).Print();
return std::cin.get();
}
Приведенный выше код компилируется и запускается, как задумано, но если я удаляю / комментирую const-ref-ctor класса-оболочки A, ни мой компилятор VS2012, ни мой компилятор gcc4.7.2 не компилируют код. (1) Что я делаю не так?
Поскольку у меня нет хорошей документации для c ++ 11, я предполагаю, что переменная копия ctor кортежа принимает только const ref другого кортежа. Если это так, (2), почему класс кортежей не имеет такого ctor? Я имею в виду основную причину.
Подводя итог, я хочу объединить все impls и оболочки в кортеж, чтобы я мог выделить одно действие (то есть make_shared). Кортеж является обязательным, потому что я написал несколько помощников, чтобы я мог искать по типу во время компиляции (например, Get<A<int>>(w)
) (3) Есть ли удобный способ хранить ссылку на импл, чтобы мне не нужно было выделять каждый импл отдельно?
Копирующий конструктор std::tuple
даже конвертирующий, очевидно, копирует все элементы, и, поскольку копия не должна изменять скопированный элемент, они помечаются как const
, Такое поведение вполне разумно, большую часть времени.
Обходной путь для вашего особого случая немного сложнее, чем вам может понравиться, но он работает. Основная идея заключается в том, что концептуально вы не хотите копировать кортеж, но вы хотите использовать его элементы в качестве инициализатора для элементов вашего другого кортежа, как такового. const
Несс должен быть сохранен.
template<unsigned...> struct seq{};
template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>{};
template<unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...>{};
namespace aux{
template<class... Ts, unsigned... Is>
std::tuple<Ts&...> tie_all_(std::tuple<Ts...>& other, seq<Is...>){
return std::tie(std::get<Is>(other)...);
}
} // aux::
template<class... Ts>
std::tuple<Ts&...> tie_all(std::tuple<Ts...>& other){
return aux::tie_all_(other, gen_seq<sizeof...(Ts)>());
}
Код используется так: WrapperTuple w(tie_all(t));
, Теперь вы можете избавиться от Member const&
конструктор.
Вы могли бы даже пойти дальше и написать функцию, которая превращает кортеж в кортеж-обертку, избавляя, таким образом, от необходимости указывать тип вручную:
template<class... Ts>
std::tuple<A<Ts>...> wrap_all(std::tuple<Ts...>& other){
return tie_all(other);
}
// ...
auto w(wrap_all(t));
И если у вас есть разные классы-обертки:
template<template<class> class Wrapper, class... Ts>
std::tuple<Wrapper<Ts>...> wrap_all_in(std::tuple<Ts...>& other){
return tie_all(other);
}
// ...
auto w = wrap_all_in<A>(t);
Почему бы тебе просто не создать wrapped_tuple
шаблон, который оборачивает весь tuple
и обеспечивает реализацию get
? Кажется излишним хранить ссылки на отдельные элементы кортежа, так как кортеж имеет фиксированную компоновку, и компилятор может выдавать тривиальный код для ссылки на отдельный элемент, заданный ссылкой на кортеж.
Например (и делать это без вариадических шаблонов, что немного раздражает):
template<typename Tuple> class wrapped_tuple;
template<size_t I, typename Tuple>
typename std::tuple_element<I, Tuple>::type&
Get(wrapped_tuple<Tuple>& w) {
return std::get<I>(w.tuple_);
}
template<typename Tuple> class wrapped_tuple {
template<size_t I, typename Uple>
friend typename std::tuple_element<I, Uple>::type&
::Get(wrapped_tuple<Uple>& w);
public:
wrapped_tuple(Tuple& t) : tuple_(t) {}
private:
Tuple& tuple_;
};
template<typename Tuple>
wrapped_tuple<Tuple> wrap_tuple(Tuple& tup) {
return wrapped_tuple<Tuple>(tup);
}
Вот на идеоне.