Commit b655b8fc by Jonathan Wakely Committed by Jonathan Wakely

Refactor std::optional SFINAE constraints

	* include/std/optional (_Optional_payload): Use variable templates
	for conditions in default template arguments and exception
	specifications.
	(optional): Likewise. Adjust indentation.
	(optional::__not_self, optional::__not_tag, optional::_Requires): New
	SFINAE helpers.
	(optional::optional): Use new helpers in constructor constraints.
	* include/std/type_traits (__or_v, __and_v): New variable templates.
	* testsuite/20_util/optional/cons/value_neg.cc: Change dg-error to
	dg-prune-output. Remove unused header.

From-SVN: r263657
parent 285ee4d0
2018-08-20 Jonathan Wakely <jwakely@redhat.com>
* include/std/optional (_Optional_payload): Use variable templates
for conditions in default template arguments and exception
specifications.
(optional): Likewise. Adjust indentation.
(optional::__not_self, optional::__not_tag, optional::_Requires): New
SFINAE helpers.
(optional::optional): Use new helpers in constructor constraints.
* include/std/type_traits (__or_v, __and_v): New variable templates.
* testsuite/20_util/optional/cons/value_neg.cc: Change dg-error to
dg-prune-output. Remove unused header.
2018-08-18 François Dumont <fdumont@gcc.gnu.org> 2018-08-18 François Dumont <fdumont@gcc.gnu.org>
* testsuite/25_algorithms/copy/86658.cc: Use dg-options to define * testsuite/25_algorithms/copy/86658.cc: Use dg-options to define
......
...@@ -102,11 +102,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -102,11 +102,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// Payload for optionals with non-trivial destructor. // Payload for optionals with non-trivial destructor.
template <typename _Tp, template <typename _Tp,
bool /*_HasTrivialDestructor*/ = bool /*_HasTrivialDestructor*/ =
is_trivially_destructible<_Tp>::value, is_trivially_destructible_v<_Tp>,
bool /*_HasTrivialCopyAssignment*/ = bool /*_HasTrivialCopyAssignment*/ =
is_trivially_copy_assignable<_Tp>::value, is_trivially_copy_assignable_v<_Tp>,
bool /*_HasTrivialMoveAssignment*/ = bool /*_HasTrivialMoveAssignment*/ =
is_trivially_move_assignable<_Tp>::value> is_trivially_move_assignable_v<_Tp>>
struct _Optional_payload struct _Optional_payload
{ {
constexpr _Optional_payload() noexcept : _M_empty() { } constexpr _Optional_payload() noexcept : _M_empty() { }
...@@ -165,8 +165,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -165,8 +165,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Optional_payload& _Optional_payload&
operator=(_Optional_payload&& __other) operator=(_Optional_payload&& __other)
noexcept(__and_<is_nothrow_move_constructible<_Tp>, noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
is_nothrow_move_assignable<_Tp>>()) is_nothrow_move_assignable<_Tp>>)
{ {
if (this->_M_engaged && __other._M_engaged) if (this->_M_engaged && __other._M_engaged)
this->_M_get() = std::move(__other._M_get()); this->_M_get() = std::move(__other._M_get());
...@@ -199,7 +199,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -199,7 +199,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename... _Args> template<typename... _Args>
void void
_M_construct(_Args&&... __args) _M_construct(_Args&&... __args)
noexcept(is_nothrow_constructible<_Stored_type, _Args...>()) noexcept(is_nothrow_constructible_v<_Stored_type, _Args...>)
{ {
::new ((void *) std::__addressof(this->_M_payload)) ::new ((void *) std::__addressof(this->_M_payload))
_Stored_type(std::forward<_Args>(__args)...); _Stored_type(std::forward<_Args>(__args)...);
...@@ -377,7 +377,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -377,7 +377,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename... _Args> template<typename... _Args>
void void
_M_construct(_Args&&... __args) _M_construct(_Args&&... __args)
noexcept(is_nothrow_constructible<_Stored_type, _Args...>()) noexcept(is_nothrow_constructible_v<_Stored_type, _Args...>)
{ {
::new ((void *) std::__addressof(this->_M_payload)) ::new ((void *) std::__addressof(this->_M_payload))
_Stored_type(std::forward<_Args>(__args)...); _Stored_type(std::forward<_Args>(__args)...);
...@@ -468,8 +468,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -468,8 +468,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Optional_payload& _Optional_payload&
operator=(_Optional_payload&& __other) operator=(_Optional_payload&& __other)
noexcept(__and_<is_nothrow_move_constructible<_Tp>, noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
is_nothrow_move_assignable<_Tp>>()) is_nothrow_move_assignable<_Tp>>)
{ {
if (this->_M_engaged && __other._M_engaged) if (this->_M_engaged && __other._M_engaged)
this->_M_get() = std::move(__other._M_get()); this->_M_get() = std::move(__other._M_get());
...@@ -496,7 +496,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -496,7 +496,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename... _Args> template<typename... _Args>
void void
_M_construct(_Args&&... __args) _M_construct(_Args&&... __args)
noexcept(is_nothrow_constructible<_Stored_type, _Args...>()) noexcept(is_nothrow_constructible_v<_Stored_type, _Args...>)
{ {
::new ((void *) std::__addressof(this->_M_payload)) ::new ((void *) std::__addressof(this->_M_payload))
_Stored_type(std::forward<_Args>(__args)...); _Stored_type(std::forward<_Args>(__args)...);
...@@ -598,8 +598,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -598,8 +598,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Optional_payload& _Optional_payload&
operator=(_Optional_payload&& __other) operator=(_Optional_payload&& __other)
noexcept(__and_<is_nothrow_move_constructible<_Tp>, noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
is_nothrow_move_assignable<_Tp>>()) is_nothrow_move_assignable<_Tp>>)
{ {
if (this->_M_engaged && __other._M_engaged) if (this->_M_engaged && __other._M_engaged)
this->_M_get() = std::move(__other._M_get()); this->_M_get() = std::move(__other._M_get());
...@@ -626,7 +626,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -626,7 +626,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename... _Args> template<typename... _Args>
void void
_M_construct(_Args&&... __args) _M_construct(_Args&&... __args)
noexcept(is_nothrow_constructible<_Stored_type, _Args...>()) noexcept(is_nothrow_constructible_v<_Stored_type, _Args...>)
{ {
::new ((void *) std::__addressof(this->_M_payload)) ::new ((void *) std::__addressof(this->_M_payload))
_Stored_type(std::forward<_Args>(__args)...); _Stored_type(std::forward<_Args>(__args)...);
...@@ -665,7 +665,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -665,7 +665,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename... _Args> template<typename... _Args>
void void
_M_construct(_Args&&... __args) _M_construct(_Args&&... __args)
noexcept(is_nothrow_constructible<_Stored_type, _Args...>()) noexcept(is_nothrow_constructible_v<_Stored_type, _Args...>)
{ {
::new ::new
(std::__addressof(static_cast<_Dp*>(this)->_M_payload._M_payload)) (std::__addressof(static_cast<_Dp*>(this)->_M_payload._M_payload))
...@@ -735,7 +735,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -735,7 +735,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ } { }
constexpr _Optional_base(_Optional_base&& __other) constexpr _Optional_base(_Optional_base&& __other)
noexcept(is_nothrow_move_constructible<_Tp>()) noexcept(is_nothrow_move_constructible_v<_Tp>)
: _M_payload(__other._M_payload._M_engaged, : _M_payload(__other._M_payload._M_engaged,
std::move(__other._M_payload)) std::move(__other._M_payload))
{ } { }
...@@ -864,7 +864,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -864,7 +864,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
constexpr _Optional_base(const _Optional_base& __other) = default; constexpr _Optional_base(const _Optional_base& __other) = default;
constexpr _Optional_base(_Optional_base&& __other) constexpr _Optional_base(_Optional_base&& __other)
noexcept(is_nothrow_move_constructible<_Tp>()) noexcept(is_nothrow_move_constructible_v<_Tp>)
: _M_payload(__other._M_payload._M_engaged, : _M_payload(__other._M_payload._M_engaged,
std::move(__other._M_payload)) std::move(__other._M_payload))
{ } { }
...@@ -986,13 +986,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -986,13 +986,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: private _Optional_base<_Tp>, : private _Optional_base<_Tp>,
private _Enable_copy_move< private _Enable_copy_move<
// Copy constructor. // Copy constructor.
is_copy_constructible<_Tp>::value, is_copy_constructible_v<_Tp>,
// Copy assignment. // Copy assignment.
__and_<is_copy_constructible<_Tp>, is_copy_assignable<_Tp>>::value, __and_v<is_copy_constructible<_Tp>, is_copy_assignable<_Tp>>,
// Move constructor. // Move constructor.
is_move_constructible<_Tp>::value, is_move_constructible_v<_Tp>,
// Move assignment. // Move assignment.
__and_<is_move_constructible<_Tp>, is_move_assignable<_Tp>>::value, __and_v<is_move_constructible<_Tp>, is_move_assignable<_Tp>>,
// Unique tag type. // Unique tag type.
optional<_Tp>> optional<_Tp>>
{ {
...@@ -1003,6 +1003,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1003,6 +1003,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
private: private:
using _Base = _Optional_base<_Tp>; using _Base = _Optional_base<_Tp>;
// SFINAE helpers
template<typename _Up>
using __not_self = __not_<is_same<optional, __remove_cvref_t<_Up>>>;
template<typename _Up>
using __not_tag = __not_<is_same<in_place_t, __remove_cvref_t<_Up>>>;
template<typename... _Cond>
using _Requires = enable_if_t<__and_v<_Cond...>, bool>;
public: public:
using value_type = _Tp; using value_type = _Tp;
...@@ -1011,90 +1019,82 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1011,90 +1019,82 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
constexpr optional(nullopt_t) noexcept { } constexpr optional(nullopt_t) noexcept { }
// Converting constructors for engaged optionals. // Converting constructors for engaged optionals.
template <typename _Up = _Tp, template<typename _Up = _Tp,
enable_if_t<__and_< _Requires<__not_self<_Up>, __not_tag<_Up>,
__not_<is_same<optional<_Tp>, decay_t<_Up>>>,
__not_<is_same<in_place_t, decay_t<_Up>>>,
is_constructible<_Tp, _Up&&>, is_constructible<_Tp, _Up&&>,
is_convertible<_Up&&, _Tp> is_convertible<_Up&&, _Tp>> = true>
>::value, bool> = true> constexpr
constexpr optional(_Up&& __t) optional(_Up&& __t)
: _Base(std::in_place, std::forward<_Up>(__t)) { } : _Base(std::in_place, std::forward<_Up>(__t)) { }
template <typename _Up = _Tp, template<typename _Up = _Tp,
enable_if_t<__and_< _Requires<__not_self<_Up>, __not_tag<_Up>,
__not_<is_same<optional<_Tp>, decay_t<_Up>>>,
__not_<is_same<in_place_t, decay_t<_Up>>>,
is_constructible<_Tp, _Up&&>, is_constructible<_Tp, _Up&&>,
__not_<is_convertible<_Up&&, _Tp>> __not_<is_convertible<_Up&&, _Tp>>> = false>
>::value, bool> = false> explicit constexpr
explicit constexpr optional(_Up&& __t) optional(_Up&& __t)
: _Base(std::in_place, std::forward<_Up>(__t)) { } : _Base(std::in_place, std::forward<_Up>(__t)) { }
template <typename _Up, template<typename _Up,
enable_if_t<__and_< _Requires<__not_<is_same<_Tp, _Up>>,
__not_<is_same<_Tp, _Up>>,
is_constructible<_Tp, const _Up&>, is_constructible<_Tp, const _Up&>,
is_convertible<const _Up&, _Tp>, is_convertible<const _Up&, _Tp>,
__not_<__converts_from_optional<_Tp, _Up>> __not_<__converts_from_optional<_Tp, _Up>>> = true>
>::value, bool> = true> constexpr
constexpr optional(const optional<_Up>& __t) optional(const optional<_Up>& __t)
{ {
if (__t) if (__t)
emplace(*__t); emplace(*__t);
} }
template <typename _Up, template<typename _Up,
enable_if_t<__and_< _Requires<__not_<is_same<_Tp, _Up>>,
__not_<is_same<_Tp, _Up>>,
is_constructible<_Tp, const _Up&>, is_constructible<_Tp, const _Up&>,
__not_<is_convertible<const _Up&, _Tp>>, __not_<is_convertible<const _Up&, _Tp>>,
__not_<__converts_from_optional<_Tp, _Up>> __not_<__converts_from_optional<_Tp, _Up>>> = false>
>::value, bool> = false> explicit constexpr
explicit constexpr optional(const optional<_Up>& __t) optional(const optional<_Up>& __t)
{ {
if (__t) if (__t)
emplace(*__t); emplace(*__t);
} }
template <typename _Up, template <typename _Up,
enable_if_t<__and_< _Requires<__not_<is_same<_Tp, _Up>>,
__not_<is_same<_Tp, _Up>>,
is_constructible<_Tp, _Up&&>, is_constructible<_Tp, _Up&&>,
is_convertible<_Up&&, _Tp>, is_convertible<_Up&&, _Tp>,
__not_<__converts_from_optional<_Tp, _Up>> __not_<__converts_from_optional<_Tp, _Up>>> = true>
>::value, bool> = true> constexpr
constexpr optional(optional<_Up>&& __t) optional(optional<_Up>&& __t)
{ {
if (__t) if (__t)
emplace(std::move(*__t)); emplace(std::move(*__t));
} }
template <typename _Up, template <typename _Up,
enable_if_t<__and_< _Requires<__not_<is_same<_Tp, _Up>>,
__not_<is_same<_Tp, _Up>>,
is_constructible<_Tp, _Up&&>, is_constructible<_Tp, _Up&&>,
__not_<is_convertible<_Up&&, _Tp>>, __not_<is_convertible<_Up&&, _Tp>>,
__not_<__converts_from_optional<_Tp, _Up>> __not_<__converts_from_optional<_Tp, _Up>>> = false>
>::value, bool> = false> explicit constexpr
explicit constexpr optional(optional<_Up>&& __t) optional(optional<_Up>&& __t)
{ {
if (__t) if (__t)
emplace(std::move(*__t)); emplace(std::move(*__t));
} }
template<typename... _Args, template<typename... _Args,
enable_if_t<is_constructible_v<_Tp, _Args&&...>, bool> = false> _Requires<is_constructible<_Tp, _Args&&...>> = false>
explicit constexpr optional(in_place_t, _Args&&... __args) explicit constexpr
optional(in_place_t, _Args&&... __args)
: _Base(std::in_place, std::forward<_Args>(__args)...) { } : _Base(std::in_place, std::forward<_Args>(__args)...) { }
template<typename _Up, typename... _Args, template<typename _Up, typename... _Args,
enable_if_t<is_constructible_v<_Tp, _Requires<is_constructible<_Tp,
initializer_list<_Up>&, initializer_list<_Up>&,
_Args&&...>, bool> = false> _Args&&...>> = false>
explicit constexpr optional(in_place_t, explicit constexpr
initializer_list<_Up> __il, optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
_Args&&... __args)
: _Base(std::in_place, __il, std::forward<_Args>(__args)...) { } : _Base(std::in_place, __il, std::forward<_Args>(__args)...) { }
// Assignment operators. // Assignment operators.
...@@ -1106,12 +1106,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1106,12 +1106,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} }
template<typename _Up = _Tp> template<typename _Up = _Tp>
enable_if_t<__and_< enable_if_t<__and_v<__not_self<_Up>,
__not_<is_same<optional<_Tp>, decay_t<_Up>>>,
is_constructible<_Tp, _Up>,
__not_<__and_<is_scalar<_Tp>, __not_<__and_<is_scalar<_Tp>,
is_same<_Tp, decay_t<_Up>>>>, is_same<_Tp, decay_t<_Up>>>>,
is_assignable<_Tp&, _Up>>::value, is_constructible<_Tp, _Up>,
is_assignable<_Tp&, _Up>>,
optional&> optional&>
operator=(_Up&& __u) operator=(_Up&& __u)
{ {
...@@ -1124,13 +1123,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1124,13 +1123,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} }
template<typename _Up> template<typename _Up>
enable_if_t<__and_< enable_if_t<__and_v<__not_<is_same<_Tp, _Up>>,
__not_<is_same<_Tp, _Up>>,
is_constructible<_Tp, const _Up&>, is_constructible<_Tp, const _Up&>,
is_assignable<_Tp&, _Up>, is_assignable<_Tp&, _Up>,
__not_<__converts_from_optional<_Tp, _Up>>, __not_<__converts_from_optional<_Tp, _Up>>,
__not_<__assigns_from_optional<_Tp, _Up>> __not_<__assigns_from_optional<_Tp, _Up>>>,
>::value,
optional&> optional&>
operator=(const optional<_Up>& __u) operator=(const optional<_Up>& __u)
{ {
...@@ -1149,13 +1146,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1149,13 +1146,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} }
template<typename _Up> template<typename _Up>
enable_if_t<__and_< enable_if_t<__and_v<__not_<is_same<_Tp, _Up>>,
__not_<is_same<_Tp, _Up>>,
is_constructible<_Tp, _Up>, is_constructible<_Tp, _Up>,
is_assignable<_Tp&, _Up>, is_assignable<_Tp&, _Up>,
__not_<__converts_from_optional<_Tp, _Up>>, __not_<__converts_from_optional<_Tp, _Up>>,
__not_<__assigns_from_optional<_Tp, _Up>> __not_<__assigns_from_optional<_Tp, _Up>>>,
>::value,
optional&> optional&>
operator=(optional<_Up>&& __u) operator=(optional<_Up>&& __u)
{ {
...@@ -1175,7 +1170,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1175,7 +1170,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} }
template<typename... _Args> template<typename... _Args>
enable_if_t<is_constructible<_Tp, _Args&&...>::value, _Tp&> enable_if_t<is_constructible_v<_Tp, _Args&&...>, _Tp&>
emplace(_Args&&... __args) emplace(_Args&&... __args)
{ {
this->_M_reset(); this->_M_reset();
...@@ -1184,8 +1179,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1184,8 +1179,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} }
template<typename _Up, typename... _Args> template<typename _Up, typename... _Args>
enable_if_t<is_constructible<_Tp, initializer_list<_Up>&, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&,
_Args&&...>::value, _Tp&> _Args&&...>, _Tp&>
emplace(initializer_list<_Up> __il, _Args&&... __args) emplace(initializer_list<_Up> __il, _Args&&... __args)
{ {
this->_M_reset(); this->_M_reset();
...@@ -1198,7 +1193,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1198,7 +1193,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// Swap. // Swap.
void void
swap(optional& __other) swap(optional& __other)
noexcept(is_nothrow_move_constructible<_Tp>() noexcept(is_nothrow_move_constructible_v<_Tp>
&& is_nothrow_swappable_v<_Tp>) && is_nothrow_swappable_v<_Tp>)
{ {
using std::swap; using std::swap;
...@@ -1307,6 +1302,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1307,6 +1302,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
? std::move(this->_M_get()) ? std::move(this->_M_get())
: static_cast<_Tp>(std::forward<_Up>(__u)); : static_cast<_Tp>(std::forward<_Up>(__u));
} }
void reset() noexcept { this->_M_reset(); } void reset() noexcept { this->_M_reset(); }
}; };
......
...@@ -144,6 +144,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -144,6 +144,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
template<typename... _Bn>
inline constexpr bool __or_v = __or_<_Bn...>::value;
template<typename... _Bn>
inline constexpr bool __and_v = __and_<_Bn...>::value;
#define __cpp_lib_logical_traits 201510 #define __cpp_lib_logical_traits 201510
template<typename... _Bn> template<typename... _Bn>
......
...@@ -19,8 +19,6 @@ ...@@ -19,8 +19,6 @@
// <http://www.gnu.org/licenses/>. // <http://www.gnu.org/licenses/>.
#include <optional> #include <optional>
#include <testsuite_hooks.h>
#include <string> #include <string>
#include <memory> #include <memory>
...@@ -37,8 +35,6 @@ int main() ...@@ -37,8 +35,6 @@ int main()
std::optional<std::unique_ptr<int>> oup2 = new int; // { dg-error "conversion" } std::optional<std::unique_ptr<int>> oup2 = new int; // { dg-error "conversion" }
struct U { explicit U(std::in_place_t); }; struct U { explicit U(std::in_place_t); };
std::optional<U> ou(std::in_place); // { dg-error "no matching" } std::optional<U> ou(std::in_place); // { dg-error "no matching" }
// { dg-error "no type" "" { target { *-*-* } } 1020 }
// { dg-error "no type" "" { target { *-*-* } } 1030 }
// { dg-error "no type" "" { target { *-*-* } } 1087 }
} }
} }
// { dg-prune-output "no type .*enable_if" }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment