元组中的构造函数参数

Constructor arguments from a tuple

本文关键字:参数 构造函数 元组      更新时间:2023-10-16

如果我有一个类似的结构

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

有更好的方法吗?

我们可以创建一个通用工厂函数,用于在结构化绑定世界中从类元组类型(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()

如果您使用的是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));
}

您可以使用std::tie:

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

以下是其他方法:

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);
}

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

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;
  }
};

希望这能有所帮助。

喜欢C++17模板参数推导和使用代理对象(底部的用法示例):

#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。元组参数将被传递到构造函数中。