我有一些实现者类(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();
}
上面的代码按预期编译和运行,但如果我删除/注释掉包装类A的const-ref-ctor,我的VS2012编译器和我的gcc4.7.2编译器都不会编译代码。 (1)我做错了什么?
由于我没有c ++ 11的良好文档,我猜元组的可变复制ctor只接受其他元组的const ref。如果是这样,(2)为什么没有元组类有这样的ctor?我的意思是背后的主要原因。
总结一下,我想把所有的impls和包装器放在一个元组中,这样我就可以用一个动作分配(即make_shared)。元组有点必须,因为我已经编写了一些助手,以便我可以在编译时按类型查找(例如Get<A<int>>(w)
)(3)是否有一种巧妙的方法来保存对impl的引用,这样我就不需要了分别分配每个impl。
答案 0 :(得分:3)
std::tuple
的复制构造函数,甚至是转换构造函数,显然会复制所有元素,并且由于副本不应更改复制的元素,因此它们标记为const
。在大多数情况下,这种行为是完全合理的。
您的特殊情况的解决方法更多地涉及您可能喜欢的内容,但它有效。基本的想法是,从概念上讲,您不想复制元组,但是您希望将其元素用作其他元组元素的初始化器,因此应保留它们的const
。< / p>
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);
答案 1 :(得分:1)
为什么不创建包裹整个wrapped_tuple
的{{1}}模板,并提供tuple
的实现?保持对单个元组元素的引用似乎是多余的,因为元组具有固定的布局,并且编译器可以发出简单的代码来引用给定对元组的引用的单个元素。
例如(并且这样做没有可变参数模板,这有点烦人):
get
Here关于ideone。