Merge pull request #17 from furfurylic/conditional_explicit_replace_if_xxxx

Make ctors of replace_if_conversion_failed and replace_if_skipped conditionally explicit
This commit is contained in:
furfurylic 2024-08-25 09:49:57 +09:00 committed by GitHub
commit d473af2e21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 84 additions and 10 deletions

View File

@ -2,7 +2,7 @@
<?xml-stylesheet type="text/xsl" href="Commata.xsl"?>
<document>
<title>Specification of Commata, which is just another C++17 CSV parser</title>
<signature>2024-08-19 (UTC)</signature>
<signature>2024-08-23 (UTC)</signature>
<section id="introduction">
<name>Introduction</name>
@ -1921,7 +1921,7 @@ namespace commata {
<c>// <n><xref id="replace_if_conversion_failed.cons"/>, construct/copy/destroy:</n></c>
template &lt;class All = T>
explicit replace_if_conversion_failed(const All&amp; for_all = All()) noexcept(<nc>see below</nc>);
<nc>EXPLICIT</nc> replace_if_conversion_failed(const All&amp; for_all = All()) noexcept(<nc>see below</nc>);
template &lt;class Empty, class AllButEmpty>
replace_if_conversion_failed(Empty&amp;&amp; on_empty,
const AllButEmpty&amp; for_all_but_empty) noexcept(<nc>see below</nc>);
@ -2036,11 +2036,12 @@ static constexpr std::size_t size = <nc>see below</nc>;
<code-item>
<code>
template &lt;class All = T>
explicit replace_if_conversion_failed(const All&amp; for_all = All()) noexcept(<nc>see below</nc>);
<nc>EXPLICIT</nc> replace_if_conversion_failed(const All&amp; for_all = All()) noexcept(<nc>see below</nc>);
</code>
<effects>Arranges all configurable replacement actions from <c>for_all</c>.</effects>
<remark>This overload shall not participate in overload resolution unless <c><nc>IS_ACCEPTABLE</nc>&lt;const All&amp;></c> is <c>true</c>.
The expression inside <c>noexcept</c> is equvalent to <c><nc>IS_NOTHROW_ACCEPTABLE</nc>&lt;T, const All&amp;></c>.</remark>
The expression inside <c>noexcept</c> is equvalent to <c><nc>IS_NOTHROW_ACCEPTABLE</nc>&lt;T, const All&amp;></c>.
This constructor is explicit if and only if <c>std::is_convertible_v&lt;const All&amp;, T></c> is <c>false</c>.</remark>
</code-item>
<code-item>
@ -6951,7 +6952,7 @@ namespace commata {
<c>// <n><xref id="replace_if_skipped.cons"/>, construct/copy/destroy:</n></c>
template &lt;class U = T>
explicit replace_if_skipped(U&amp;&amp; u = std::decay_t&lt;U>()) noexcept(<nc>see below</nc>);
<nc>EXPLICIT</nc> replace_if_skipped(U&amp;&amp; u = std::decay_t&lt;U>()) noexcept(<nc>see below</nc>);
explicit replace_if_skipped(replacement_fail_t) noexcept;
explicit replace_if_skipped(replacement_ignore_t) noexcept;
replace_if_skipped(const replace_if_skipped&amp; other) noexcept(<nc>see below</nc>);
@ -6990,14 +6991,15 @@ namespace commata {
<code-item>
<code>
template &lt;class U = T>
explicit replace_if_skipped(U&amp;&amp; u = std::decay_t&lt;U>()) noexcept(<nc>see below</nc>);
<nc>EXPLICIT</nc> replace_if_skipped(U&amp;&amp; u = std::decay_t&lt;U>()) noexcept(<nc>see below</nc>);
</code>
<effects>Configures the replacement action to be <c>copy</c> with an object of <c>T</c> constructed from <c>std::forward&lt;U>(u)</c>.</effects>
<remark>This constructor shall not participate in overload resolution unless <c>std::is_constructible_v&lt;T, U></c> is <c>true</c>,
<c>std::is_base_of_v&lt;replace_if_skipped&lt;T>, std::decay&lt;U>></c> is <c>false</c>,
<c>std::is_base_of_v&lt;replacement_fail_t, std::decay&lt;U>></c> is <c>false</c>, and
<c>std::is_base_of_v&lt;replacement_ignore_t, std::decay&lt;U>></c> is <c>false</c>.
The expression inside <c>noexcept</c> is equivalent to <c>std::is_nothrow_constructible_v&lt;T, U></c>.</remark>
The expression inside <c>noexcept</c> is equivalent to <c>std::is_nothrow_constructible_v&lt;T, U></c>.
This constructor is explicit if and only if <c>std::is_convertible_v&lt;U&amp;&amp;, T></c> is <c>false</c>.</remark>
</code-item>
<code-item>

View File

@ -235,6 +235,7 @@ public:
template <class U = T,
std::enable_if_t<
std::is_constructible_v<T, U>
&& !std::is_convertible_v<U&&, T>
&& !(std::is_base_of_v<replace_if_skipped, std::decay_t<U>>
|| std::is_base_of_v<replacement_fail_t, std::decay_t<U>>
|| std::is_base_of_v<replacement_ignore_t, std::decay_t<U>>)>*
@ -244,6 +245,19 @@ public:
store_(generic_args_t(), std::forward<U>(u))
{}
template <class U = T,
std::enable_if_t<
std::is_constructible_v<T, U>
&& std::is_convertible_v<U&&, T>
&& !(std::is_base_of_v<replace_if_skipped, std::decay_t<U>>
|| std::is_base_of_v<replacement_fail_t, std::decay_t<U>>
|| std::is_base_of_v<replacement_ignore_t, std::decay_t<U>>)>*
= nullptr>
replace_if_skipped(U&& u = std::decay_t<U>())
noexcept(std::is_nothrow_constructible_v<T, U>) :
store_(generic_args_t(), std::forward<U>(u))
{}
explicit replace_if_skipped(replacement_fail_t) noexcept :
store_(detail::replace_mode::fail)
{}

View File

@ -830,11 +830,19 @@ template <class T>
struct base<T, 3> : base_base<T, 3>
{
template <class All = T,
std::enable_if_t<is_acceptable_arg_v<T, const All&>>* = nullptr>
std::enable_if_t<!std::is_convertible_v<const All&, T>
&& is_acceptable_arg_v<T, const All&>>* = nullptr>
explicit base(const All& for_all = All()) :
base(for_all, for_all, for_all)
{}
template <class All = T,
std::enable_if_t<std::is_convertible_v<const All&, T>
&& is_acceptable_arg_v<T, const All&>>* = nullptr>
base(const All& for_all = All()) :
base(for_all, for_all, for_all)
{}
template <class Empty, class AllButEmpty,
std::enable_if_t<
is_acceptable_arg_v<T, Empty&&>
@ -869,11 +877,19 @@ template <class T>
struct base<T, 4> : base_base<T, 4>
{
template <class All = T,
std::enable_if_t<is_acceptable_arg_v<T, const All&>>* = nullptr>
std::enable_if_t<!std::is_convertible_v<const All&, T>
&& is_acceptable_arg_v<T, const All&>>* = nullptr>
explicit base(const All& for_all = All()) :
base(for_all, for_all, for_all, for_all)
{}
template <class All = T,
std::enable_if_t<std::is_convertible_v<const All&, T>
&& is_acceptable_arg_v<T, const All&>>* = nullptr>
base(const All& for_all = All()) :
base(for_all, for_all, for_all, for_all)
{}
template <class Empty, class AllButEmpty,
std::enable_if_t<
is_acceptable_arg_v<T, Empty&&>
@ -925,11 +941,19 @@ template <class T>
struct base<T, 5> : base_base<T, 5>
{
template <class All = T,
std::enable_if_t<is_acceptable_arg_v<T, const All&>>* = nullptr>
std::enable_if_t<!std::is_convertible_v<const All&, T>
&& is_acceptable_arg_v<T, const All&>>* = nullptr>
explicit base(const All& for_all = All()) :
base(for_all, for_all, for_all, for_all, for_all)
{}
template <class All = T,
std::enable_if_t<std::is_convertible_v<const All&, T>
&& is_acceptable_arg_v<T, const All&>>* = nullptr>
base(const All& for_all = All()) :
base(for_all, for_all, for_all, for_all, for_all)
{}
template <class Empty, class AllButEmpty,
std::enable_if_t<
is_acceptable_arg_v<T, Empty&&>

View File

@ -1270,6 +1270,25 @@ TEST_F(TestTableScannerReference, RecordEndScanner)
ASSERT_EQ(-12345, v[3]);
}
namespace {
struct B
{};
struct D : B
{};
struct E
{
explicit E(const B&)
{}
};
static_assert(std::is_convertible_v<D, replace_if_skipped<B>>);
static_assert(!std::is_convertible_v<B, replace_if_skipped<E>>);
} // end unnamed
struct TestReplaceIfSkipped : BaseTest
{};

View File

@ -164,6 +164,21 @@ namespace {
using ReplacedTypes = testing::Types<double, std::string>;
struct B
{};
struct D : B
{};
struct E
{
explicit E(const B&)
{}
};
static_assert(std::is_convertible_v<D, replace_if_conversion_failed<B>>);
static_assert(!std::is_convertible_v<B, replace_if_conversion_failed<E>>);
} // end unnamed
TYPED_TEST_SUITE(TestReplaceIfConversionFailed, ReplacedTypes);