Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<variant>: P0608R3 Improving variant's converting constructor/assignment #1629

Merged
merged 19 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions stl/inc/variant
Original file line number Diff line number Diff line change
Expand Up @@ -967,10 +967,24 @@ using _Variant_destroy_layer = conditional_t<conjunction_v<is_trivially_destruct
#pragma warning(disable : 4365) // '%s': conversion from '%s' to '%s', signed/unsigned mismatch
#pragma warning(disable : 5215) // '%s' a function parameter with volatile qualified type is deprecated in C++20
#endif // __clang__
template <size_t _Idx, class _Ty>
struct _Variant_type_test { // build Ti x[] = {std::forward<T>(t)};
template <size_t _Idx, class _Type>
static constexpr auto _Construct_array(_Type(&&)[1]) -> _Meta_list<integral_constant<size_t, _Idx>, _Type>;

template <size_t _Idx, class _Ty, class _Type>
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
using type = decltype(_Construct_array<_Idx, _Type>({_STD declval<_Ty&&>()}));
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
};

MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
template <size_t _Idx, class _Ty, class _Type>
using _Variant_type_resolver = typename _Variant_type_test::template type<_Idx, _Ty, _Type>;

template <size_t _Idx, class _Type>
struct _Variant_init_single_overload {
using _FTy = _Meta_list<integral_constant<size_t, _Idx>, _Ty> (*)(_Ty);
operator _FTy();
template <class _Ty>
using _FTy = _Variant_type_resolver<_Idx, _Ty, _Type>;

template <class _Ty>
auto operator()(_Ty&&, _Type) -> _FTy<_Ty>;
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
};

template <class _Indices, class... _Types>
Expand All @@ -987,11 +1001,12 @@ template <class Enable, class _Ty, class... _Types>
struct _Variant_init_helper {}; // failure case (has no member "type")

template <class _Ty, class... _Types>
struct _Variant_init_helper<void_t<decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty>()))>, _Ty,
struct _Variant_init_helper<
void_t<decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty&&>(), _STD declval<_Ty>()))>, _Ty,
_Types...> {
// perform overload resolution to determine the unique alternative that should be initialized in
// variant<_Types...> from an argument expression with type and value category _Ty
using type = decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty>()));
using type = decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty&&>(), _STD declval<_Ty>()));
};

template <class _Ty, class... _Types> // extract the type from _Variant_init_helper
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ tests\P0556R3_bit_integral_power_of_two_operations
tests\P0586R2_integer_comparison
tests\P0595R2_is_constant_evaluated
tests\P0607R0_inline_variables
tests\P0608R3_sane_variant_converting_constructor
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
tests\P0616R0_using_move_in_numeric
tests\P0631R8_numbers_math_constants
tests\P0660R10_jthread_and_cv_any
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_17_matrix.lst
153 changes: 153 additions & 0 deletions tests/std/tests/P0608R3_sane_variant_converting_constructor/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

// Also tests for P1957R2: Converting from T* to bool should be considered narrowing

#include <assert.h>
#include <bitset>
#include <optional>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>

using namespace std;

struct double_double {
double_double(double x) : x_(x) {}

double x_;
};
struct convertible_bool {
convertible_bool(bool) {}
~convertible_bool() = default;
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved

operator bool() {
return true;
}
};

// P0608R3 examples
static_assert(is_constructible_v<variant<string, bool>, const char*>);
static_assert(is_constructible_v<variant<string, bool>, string>);
static_assert(is_constructible_v<variant<char, optional<char16_t>>, char16_t>);
static_assert(is_constructible_v<variant<int, reference_wrapper<double>>, double&>);
static_assert(is_constructible_v<variant<float, int>, char>);
static_assert(is_constructible_v<variant<float, long int>, int>);
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
static_assert(is_constructible_v<variant<float, long long int>, int>);
static_assert(is_constructible_v<variant<float, long, double>, int>);
static_assert(is_constructible_v<variant<float, vector<int>, long long int>, int>);
static_assert(is_constructible_v<variant<float, int, long long int>, char>);

static_assert(!is_constructible_v<variant<float>, int>);
static_assert(!is_constructible_v<variant<float, vector<int>>, int>);
static_assert(!is_constructible_v<variant<float, char>, int>);

// P1957R2
static_assert(is_constructible_v<variant<bool, int>, bool>);

// more examples
static_assert(is_constructible_v<variant<double_double>, double>);
static_assert(is_constructible_v<variant<vector<vector<int>>, optional<int>, int>, int>);
static_assert(is_constructible_v<variant<vector<vector<int>>, optional<int>>, int>);
static_assert(is_constructible_v<variant<vector<int>, optional<int>, float>, int>);
static_assert(is_constructible_v<variant<bool>, convertible_bool>);
static_assert(is_constructible_v<variant<bool, int>, convertible_bool>);
static_assert(is_constructible_v<variant<convertible_bool>, bool>);
static_assert(is_constructible_v<variant<float, bool, convertible_bool>, convertible_bool>);
static_assert(is_constructible_v<variant<float, bool, convertible_bool>, bool>);
static_assert(is_constructible_v<variant<char, int>, bool>);

static_assert(!is_constructible_v<variant<double_double>, int>);
static_assert(!is_constructible_v<variant<float>, unsigned int>);
static_assert(!is_constructible_v<variant<float, long int, long long int>, int>);

void test_variant_constructor_P0608R3() {
// P0608R3 runtime checks
variant<string, bool> a = "abc"; // string
assert(a.index() == 0);
assert(get<0>(a) == string("abc"));
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved

variant<char, optional<char16_t>> b = u'\u2043'; // optional<char16_t>
assert(b.index() == 1);
assert(get<optional<char16_t>>(b) == u'\u2043');

double c_data = 3.14;
variant<int, reference_wrapper<double>> c = c_data; // reference_wrapper<double>
assert(c.index() == 1);
assert(get<1>(c) == c_data);

using T1 = variant<float, int>;
T1 d;
assert(d.index() == 0);
d = 0; // int
assert(d.index() == 1);

using T2 = variant<float, long>;
T2 e;
assert(e.index() == 0);
e = 0; // long
assert(e.index() == 1);

variant<float, int> f = 'a'; // int
assert(f.index() == 1);
assert(get<int>(f) == 97);

variant<float, long> g = 0; // long
assert(g.index() == 1);

variant<float, long, double> h = 0; // long
assert(h.index() == 1);

variant<float, vector<int>, long long int> i = 0; // long long int
assert(i.index() == 2);

variant<float, int, long long int> j = 'a'; // int
assert(j.index() == 1);
assert(get<int>(j) == 97);
}

void test_variant_constructor_P1957R2() {
bitset<4> a_bitset("0101");
bool a_data = a_bitset[2];
variant<bool, int> a = a_data; // bool
assert(a.index() == 0);
assert(get<0>(a));

bitset<4> b_bitset("0101");
variant<bool, int> b = b_bitset[2]; // bool
variant<bool, int> b2 = b_bitset[1]; // bool
assert(b.index() == 0);
assert(get<0>(b));
assert(b2.index() == 0);
assert(!get<0>(b2));
}

void test_variant_constructor_more_tests() {
variant<char, int, float, bool, vector<bool>> a = true; // bool
assert(a.index() == 3);

variant<bool, int> b = convertible_bool{true}; // bool
assert(b.index() == 0);
assert(get<0>(b));

variant<char, int, bool> c = false; // bool
assert(c.index() == 2);

variant<float, bool, convertible_bool> d = convertible_bool{true}; // convertible_bool
assert(d.index() == 2);

variant<float, bool, convertible_bool> e = bool{}; // bool
assert(e.index() == 1);
assert(!get<1>(e));

variant<float, bool> f = convertible_bool{true}; // bool
assert(f.index() == 1);
assert(get<1>(f));
}

int main() {
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
test_variant_constructor_P0608R3();
test_variant_constructor_P1957R2();
test_variant_constructor_more_tests();
}