1

我有一些实现类(impls)和一些用 C++ 实现的用户包装器。我想将 impls 和 wrappers 保存在两个不同的元组中,以便我可以通过单个分配来初始化我的 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 .

4

2 回答 2

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);
于 2012-11-29T10:56:34.860 回答
1

你为什么不直接创建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);
}

在ideone上。

于 2012-11-29T14:34:36.173 回答