| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 |
| |
| // <utility> |
| |
| // template <class T1, class T2> struct pair |
| |
| // template <pair-like P> |
| // constexpr explicit(see-below) pair(P&&); // since C++23 |
| |
| #include <array> |
| #include <cassert> |
| #include <ranges> |
| #include <string> |
| #include <tuple> |
| #include <type_traits> |
| #include <utility> |
| |
| namespace my_ns{ |
| |
| struct MyPairLike { |
| |
| template <std::size_t N> |
| friend int get(MyPairLike const&) |
| { |
| return 0; |
| } |
| |
| }; |
| |
| } // namespace my_ns |
| |
| namespace std { |
| |
| template <> |
| struct tuple_size<my_ns::MyPairLike> : std::integral_constant<std::size_t, 2> {}; |
| |
| template <std::size_t N> |
| struct tuple_element<N, my_ns::MyPairLike> { |
| using type = int; |
| }; |
| |
| } // namespace std |
| |
| // https://github.com/llvm/llvm-project/issues/65620 |
| // This used to be a hard error |
| static_assert(!std::is_constructible_v<std::pair<int,int>, my_ns::MyPairLike const&>); |
| |
| |
| constexpr bool test() { |
| // Make sure construction works from array, tuple, and ranges::subrange |
| { |
| // Check from std::array |
| { |
| std::array<int, 2> a = {1, 2}; |
| std::pair<int, int> p(a); |
| assert(p.first == 1); |
| assert(p.second == 2); |
| static_assert(!std::is_constructible_v<std::pair<int, int>, std::array<int, 1>>); // too small |
| static_assert( std::is_constructible_v<std::pair<int, int>, std::array<int, 2>>); // works (test the test) |
| static_assert(!std::is_constructible_v<std::pair<int, int>, std::array<int, 3>>); // too large |
| } |
| |
| // Check from std::tuple |
| { |
| std::tuple<int, int> a = {1, 2}; |
| std::pair<int, int> p(a); |
| assert(p.first == 1); |
| assert(p.second == 2); |
| static_assert(!std::is_constructible_v<std::pair<int, int>, std::tuple<int>>); // too small |
| static_assert( std::is_constructible_v<std::pair<int, int>, std::tuple<int, int>>); // works (test the test) |
| static_assert(!std::is_constructible_v<std::pair<int, int>, std::tuple<int, int, int>>); // too large |
| } |
| |
| // Check that the constructor excludes ranges::subrange |
| { |
| int data[] = {1, 2, 3, 4, 5}; |
| const std::ranges::subrange a(data); |
| // Note the expression below would be ambiguous if pair's |
| // constructor does not exclude subrange |
| std::pair<int*, int*> p = a; |
| assert(p.first == data + 0); |
| assert(p.second == data + 5); |
| } |
| } |
| |
| // Make sure we allow element conversion from a pair-like |
| { |
| std::tuple<int, char const*> a = {34, "hello world"}; |
| std::pair<long, std::string> p(a); |
| assert(p.first == 34); |
| assert(p.second == std::string("hello world")); |
| static_assert(!std::is_constructible_v<std::pair<long, std::string>, std::tuple<char*, std::string>>); // first not convertible |
| static_assert(!std::is_constructible_v<std::pair<long, std::string>, std::tuple<long, void*>>); // second not convertible |
| static_assert( std::is_constructible_v<std::pair<long, std::string>, std::tuple<long, std::string>>); // works (test the test) |
| } |
| |
| // Make sure we forward the pair-like elements |
| { |
| struct NoCopy { |
| NoCopy() = default; |
| NoCopy(NoCopy const&) = delete; |
| NoCopy(NoCopy&&) = default; |
| }; |
| std::tuple<NoCopy, NoCopy> a; |
| std::pair<NoCopy, NoCopy> p(std::move(a)); |
| (void)p; |
| } |
| |
| // Make sure the constructor is implicit iff both elements can be converted |
| { |
| struct To { }; |
| struct FromImplicit { |
| constexpr operator To() const { return To{}; } |
| }; |
| struct FromExplicit { |
| constexpr explicit operator To() const { return To{}; } |
| }; |
| // If both are convertible, the constructor is not explicit |
| { |
| std::tuple<FromImplicit, float> a = {FromImplicit{}, 2.3f}; |
| std::pair<To, double> p = a; |
| (void)p; |
| static_assert(std::is_convertible_v<std::tuple<FromImplicit, float>, std::pair<To, double>>); |
| } |
| // Otherwise, the constructor is explicit |
| { |
| static_assert( std::is_constructible_v<std::pair<To, int>, std::tuple<FromExplicit, int>>); |
| static_assert(!std::is_convertible_v<std::tuple<FromExplicit, int>, std::pair<To, int>>); |
| |
| static_assert( std::is_constructible_v<std::pair<int, To>, std::tuple<int, FromExplicit>>); |
| static_assert(!std::is_convertible_v<std::tuple<int, FromExplicit>, std::pair<int, To>>); |
| |
| static_assert( std::is_constructible_v<std::pair<To, To>, std::tuple<FromExplicit, FromExplicit>>); |
| static_assert(!std::is_convertible_v<std::tuple<FromExplicit, FromExplicit>, std::pair<To, To>>); |
| } |
| } |
| return true; |
| } |
| |
| int main(int, char**) { |
| test(); |
| static_assert(test()); |
| |
| return 0; |
| } |