来自元组的构造函数参数

时间:2016-07-18 15:40:12

标签: c++ struct constructor tuples

如果我有一个结构:

struct Thing
{
  int x;
  int y;
  bool a;
  bool b;
}

然后我可以通过执行Thing来创建Thing t {1,2,true,false};对象。但是,如果我有一个元组,那么我正在做类似的事情:

std::tuple<int, int, bool, bool> info = std::make_tuple(1,2,true,false);
Thing t { std::get<0>(info), std::get<1>(info).. // and so on

有更好的方法吗?

6 个答案:

答案 0 :(得分:5)

我们可以创建一个通用工厂函数,用于从类似于元组的类型(std::tuplestd::pairstd::array和结构化绑定中的任意用户定义的类元组对象创建聚合世界):

template <class T, class Tuple, size_t... Is>
T construct_from_tuple(Tuple&& tuple, std::index_sequence<Is...> ) {
    return T{std::get<Is>(std::forward<Tuple>(tuple))...};
}

template <class T, class Tuple>
T construct_from_tuple(Tuple&& tuple) {
    return construct_from_tuple<T>(std::forward<Tuple>(tuple),
        std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{}
        );
}

在您的情况下将用作:

std::tuple<int, int, bool, bool> info = std::make_tuple(1,2,true,false);
Thing t = construct_from_tuple<Thing>(info); // or std::move(info)

这样,Thing仍然可以是聚合(不必添加构造函数/赋值),我们的解决方案可以解决许多类型的问题。

作为一项改进,我们可以将SFINAE添加到两个重载中,以确保它们无法使用无效的元组类型进行调用。

在接受分解如何工作的措辞之前,可能需要将对std::get<Is>的合格调用更改为对具有特殊查找规则的get<Is>的无限制调用。目前,这是没有实际意义,因为它是2016年,我们没有结构化绑定。

更新:在C ++ 17中,有std::make_from_tuple()

答案 1 :(得分:4)

如果您使用的是c ++ 14,您可以使用std::index_sequence创建帮助函数和结构,如下所示:

#include <tuple>
#include <utility>

struct Thing
{
  int x;
  int y;
  bool a;
  bool b;
};

template <class Thi, class Tup, class I = std::make_index_sequence<std::tuple_size<Tup>::value>>
struct Creator;

template <class Thi, class Tup, size_t... Is>
struct Creator<Thi, Tup, std::index_sequence<Is...> > {
   static Thi create(const Tup &t) {
      return {std::get<Is>(t)...};
   }
};

template <class Thi, class Tup>
Thi create(const Tup &t) {
   return Creator<Thi, Tup>::create(t);
}

int main() {
   Thing thi = create<Thing>(std::make_tuple(1,2,true,false));
}

没有附加类的版本(带有一个附加功能):

#include <tuple>
#include <utility>

struct Thing
{
  int x;
  int y;
  bool a;
  bool b;
};

template <class Thi, class Tup, size_t... Is>
Thi create_impl(const Tup &t, std::index_sequence<Is...>) {
   return {std::get<Is>(t)...};
}

template <class Thi, class Tup>
Thi create(const Tup &t) {
   return create_impl<Thi, Tup>(t, std::make_index_sequence<std::tuple_size<Tup>::value>{});
}

int main() {
   Thing thi = create<Thing>(std::make_tuple(1,2,true,false));
}

又一次这个棘手的版本只有一个辅助函数:

#include <tuple>
#include <utility>

struct Thing
{
  int x;
  int y;
  bool a;
  bool b;
};

template <class R, class T, size_t... Is>
R create(const T &t, std::index_sequence<Is...> = {}) {
   if (std::tuple_size<T>::value == sizeof...(Is)) {
      return {std::get<Is>(t)...};
   }
   return create<R>(t, std::make_index_sequence<std::tuple_size<T>::value>{});
}

int main() {
   Thing thi = create<Thing>(std::make_tuple(1,2,true,false));
}

答案 2 :(得分:2)

您可以使用std::tie

Thing t;

std::tie(t.x, t.y, t.a, t.b) = info;

答案 3 :(得分:1)

提供显式构造函数和赋值运算符:

struct Thing
{
  int x;
  int y;
  bool a;
  bool b;

  Thing() { }

  Thing( int x, int y, bool a, bool b ): x(x), y(y), a(a), b(b) { }

  Thing( const std::tuple <int, int, bool, bool> & t ) 
  {
    std::tie( x, y, a, b ) = t;
  }

  Thing& operator = ( const std::tuple <int, int, bool, bool> & t ) 
  {
    std::tie( x, y, a, b ) = t;
    return *this;
  }
};

希望这有帮助。

答案 4 :(得分:1)

以下是其他方式:

struct Thing
{
    Thing(){}

    Thing(int A_, int B_, int C_, int D_) //1
        : A(A_), B(B_), C(C_), D(D_) {}

    Thing(std::tuple<int,int,bool,bool> tuple) //3
        : A(std::get<0>(tuple)), B(std::get<1>(tuple)),
          C(std::get<2>(tuple)), D(std::get<3>(tuple)) {}

    void tie_from_tuple(std::tuple<int,int,bool,bool> tuple) //4
    {
        std::tie(A,B,C,D) = tuple;
    }

    int A;
    int B;
    bool C;
    bool D;
};

inline Thing tuple_to_thing(const std::tuple<int,int,bool,bool>& tuple) //2
{
    return Thing{std::get<0>(tuple), std::get<1>(tuple),
                 std::get<2>(tuple), std::get<3>(tuple)};
}

int main()
{
    auto info = std::make_tuple(1,2,true,false);

    //1 make a constructor
    Thing one(info);
    //2 make a conversion function
    Thing second = tuple_to_thing(info);
    //3 don't use tuple (just use the struct itself if you have to pass it)
    Thing three{1,2,true,false};
    //4 make member function that uses std::tie
    Thing four;
    four.tie_from_tuple(info);
}

答案 5 :(得分:1)

喜欢C++17 template argument deduction并使用代理对象(底部使用示例):

#include <tuple>

using namespace std;

template <class Tuple>
class FromTuple {
    // static constructor, used to unpack argument_pack
    template <class Result, class From, size_t... indices>
    static constexpr Result construct(index_sequence<indices...>, From&& from_tuple) {
        return { get<indices>(forward<
                decltype(from_tuple.arguments)>(from_tuple.arguments))... };
    }

    // used to select static constructor
    using Indices = make_index_sequence<
            tuple_size_v< remove_reference_t<Tuple> >>;

public:
    // construct with actual tuple types only for parameter deduction
    explicit constexpr FromTuple(const Tuple& arguments) : arguments(arguments) {}
    explicit constexpr FromTuple(Tuple&& arguments) : arguments(move(arguments)) {}

    // implicit cast operator delegates to static constructor
    template <class Result>
    constexpr operator Result() { return construct<Result>(Indices{}, *this); }

private:
    Tuple arguments;
};

struct Thing { int x; int y; bool a; bool b; };

int main() {
    std::tuple<int, int, bool, bool> info = std::make_tuple(1,2,true,false);

    Thing thing0((Thing)FromTuple(info));
    Thing thing1{(Thing)FromTuple(info)};

    FromTuple from_info(info);
    Thing thing2(from_info); // only way to avoid implicit cast operator
    Thing thing3{(Thing)from_info};

    return 0;
}

这可以推广到任何类或结构,而不仅仅是Thing。元组参数将传递到构造函数中。