c++11通用工具:tuple

关于pair的构造函数

c++11里 std::pair<T1,T2>::pair 的构造函数有:

constexpr pair();
pair(const T1& x, const T2& y);
template <class U1, class U2> pair(U1&& x, U2&& y);
template <class U1, class U2> pair(const pair<U1, U2>& p);
template <class U1, class U2> pair(pair<U1, U2>&& p);
template <class... Args1, class... Args2>
  pair(std::piecewise_construct_t,
       std::tuple<Args1...> first_args,
       std::tuple<Args2...> second_args);
pair(const pair& p) = default;
pair(pair&& p) = default;

来自stackoverflow的一个问题 “why do i need to use piecewise_construct in map::emplace for single arg constructors of noncopyable objects?” 的解答中说明了 pair 的一些问题,以下内容是一个简述。

#include <map>
struct A
{
    A(int) {}
    A(A&&) = delete;
    A(A const&) = delete;
};
int main()
{
    std::pair<int, A> x(1, 4); // error
}

std::pair<int, A> x(1, 4); 期望使用 template <class U1, class U2> pair(U1&& x, U2&& y);,但 cppreference 对于该构造函数说:

以 std::forward(x) 初始化 first 并以 std::forward(y) 初始化 second 。
此构造函数参与重载决议,当且仅当 std::is_constructible_v<first_type, U1&&> 和 std::is_constructible_v<second_type, U2&&> 均为 true 。
此构造函数为 explicit ,当且仅当 std::is_convertible_v<U1&&, first_type> 为 false 或 std::is_convertible_v<U2&&, second_type> 为 false 。

因为A的移动构造是delete的,所以该版本的构造函数不能使用。
然后去尝试 pair(const T1& x, const T2& y);,因为A的复制构造也是delete的,这个版本的构造函数也不能用。
所以找不到可用的构造函数了。
这可咋整,注意 pair 的第6个构造函数:

转发 first_args 的元素到 first 的构造函数并转发 second_args 的元素到 second 的构造函数。这是能用于构造不可复制不可移动类型的 pair 的仅有的非默认构造函数。

std::piecewise_construct_t 就是用于这种情形的一个专用类型,std::piecewise_construct是它的一个对象。改为 std::pair<int, A> x(std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(4)); 搞定。


因为 map 的 value_type 是 pair<const K, V>,map::emplace 会将参数全部转发给 value_type 进行原地构造,所以会出现这种代码:

#include <map>
struct A
{
    A(int) {}
    A(A&&) = delete;
    A(const A&) = delete;
};
int main()
{
    std::map<int, A> map;
    map.emplace(1, 2); // doesn't work
    map.emplace(std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(2)); // OK
}

另外,对于 copy-initialization(用等号初始化):A t = 4;,会先创建一个临时的A,然后用这个临时对象初始化t,c++98里是调拷贝构造,c++11则是调移动构造。
但是,看起来只是形式上需要拷贝构造或移动构造(将他们声明为delete或private的将会编译不通过),实际上并没有调用到(通过在内部调用printf测试),或许是编译器进行了优化。

tuple

接下来介绍下 tuple。tuple 是 pair 的推广,可以保存多个不同类型的元素。
有几个函数可以用来创建 tuple:

//返回类型由参数推断而来
template <class... Types>
constexpr tuple<VTypes...> make_tuple(Types&&...);
template<class... Types>
tuple<Types&&...> forward_as_tuple (Types&&... args) noexcept;
template<class... Types>
constexpr tuple<Types&...> tie(Types&...) noexcept;
template <class... Tuples>
constexpr tuple<CTypes...> tuple_cat(Tuples&&...);

std::forward_as_tuple 创建用于转发的 tuple,上节已经用过了。
std::tie 创建到其参数或 std::ignore 实例的左值引用的 tuple,std::ignore 是用 tie 解包 tuple 时用来跳过元素的占位符,例如:

std::map<int,int> m;
bool result;
//tuple可以从pair构造或赋值,如果tuple只有两个元素的话
std::tie(std::ignore, result) = m.insert(std::make_pair(1,2));

std::tuple_cat 可以将多个 tuple 连接成新的 tuple,如:

#include <iostream>
#include <tuple>
#include <string>

template<class Tuple, std::size_t N>
struct TuplePrinter {
    static void print(const Tuple& t) 
    {
        TuplePrinter<Tuple, N-1>::print(t);
        std::cout << ", " << std::get<N-1>(t);
    }
};
 
template<class Tuple>
struct TuplePrinter<Tuple, 1> {
    static void print(const Tuple& t) 
    {
        std::cout << std::get<0>(t);
    }
};
 
template<class... Args>
void print(const std::tuple<Args...>& t) 
{
    std::cout << "(";
    TuplePrinter<decltype(t), sizeof...(Args)>::print(t);
    std::cout << ")\n";
}
 
int main()
{
    std::tuple<int, std::string, float> t1(10, "Test", 3.14);
    int n = 7;
    auto t2 = std::tuple_cat(t1, std::make_tuple("Foo", "bar"), t1, std::tie(n));
    n = 10;
    print(t2);
}
//(10, Test, 3.14, Foo, bar, 10, Test, 3.14, 10)

上面用到了 std::get,它从 tuple 中提取元素,另外它还能从 std::pair 和 std::array 中提取元素。
std::tuple_size 用来获取 tuple,pair,array 的大小:

#include <iostream>
#include <utility>
#include <tuple>
#include <array>

template<class T>
void test(T t)
{
    int a[std::tuple_size<T>::value]; // 可用于编译时
    std::cout << std::tuple_size<T>::value << '\n'; // 或运行时
}

int main()
{
    test(std::make_tuple(1, 2, 3.14));
    test(std::make_pair(1, 3.14));
    std::array<float, 3> arr;
    test(arr);
}

std::tuple_element 用来获取 tuple,pair,array 元素的类型,如:

#include <iostream>
#include <tuple>
 
template <class... Args>
struct type_list
{
   template <std::size_t N>
   using type = typename std::tuple_element<N, std::tuple<Args...>>::type;
};
 
int main()
{
   std::cout << std::boolalpha;
   type_list<int, char, bool>::type<2> x = true;
   std::cout << x << '\n';
}

参考 <tuple>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值