diff --git a/docs/CommataSpecification.xml b/docs/CommataSpecification.xml index 65bd6a4..e8ca314 100644 --- a/docs/CommataSpecification.xml +++ b/docs/CommataSpecification.xml @@ -2,7 +2,7 @@ Specification of Commata, which is just another C++17 CSV parser -2024-08-19 (UTC) +2024-08-23 (UTC)
Introduction @@ -1921,7 +1921,7 @@ namespace commata { // , construct/copy/destroy: template <class All = T> - explicit replace_if_conversion_failed(const All& for_all = All()) noexcept(see below); + EXPLICIT replace_if_conversion_failed(const All& for_all = All()) noexcept(see below); template <class Empty, class AllButEmpty> replace_if_conversion_failed(Empty&& on_empty, const AllButEmpty& for_all_but_empty) noexcept(see below); @@ -2036,11 +2036,12 @@ static constexpr std::size_t size = see below; template <class All = T> - explicit replace_if_conversion_failed(const All& for_all = All()) noexcept(see below); + EXPLICIT replace_if_conversion_failed(const All& for_all = All()) noexcept(see below); Arranges all configurable replacement actions from for_all. This overload shall not participate in overload resolution unless IS_ACCEPTABLE<const All&> is true. - The expression inside noexcept is equvalent to IS_NOTHROW_ACCEPTABLE<T, const All&>. + The expression inside noexcept is equvalent to IS_NOTHROW_ACCEPTABLE<T, const All&>. + This constructor is explicit if and only if std::is_convertible_v<const All&, T> is false. @@ -6951,7 +6952,7 @@ namespace commata { // , construct/copy/destroy: template <class U = T> - explicit replace_if_skipped(U&& u = std::decay_t<U>()) noexcept(see below); + EXPLICIT replace_if_skipped(U&& u = std::decay_t<U>()) noexcept(see below); explicit replace_if_skipped(replacement_fail_t) noexcept; explicit replace_if_skipped(replacement_ignore_t) noexcept; replace_if_skipped(const replace_if_skipped& other) noexcept(see below); @@ -6990,14 +6991,15 @@ namespace commata { template <class U = T> - explicit replace_if_skipped(U&& u = std::decay_t<U>()) noexcept(see below); + EXPLICIT replace_if_skipped(U&& u = std::decay_t<U>()) noexcept(see below); Configures the replacement action to be copy with an object of T constructed from std::forward<U>(u). This constructor shall not participate in overload resolution unless std::is_constructible_v<T, U> is true, std::is_base_of_v<replace_if_skipped<T>, std::decay<U>> is false, std::is_base_of_v<replacement_fail_t, std::decay<U>> is false, and std::is_base_of_v<replacement_ignore_t, std::decay<U>> is false. - The expression inside noexcept is equivalent to std::is_nothrow_constructible_v<T, U>. + The expression inside noexcept is equivalent to std::is_nothrow_constructible_v<T, U>. + This constructor is explicit if and only if std::is_convertible_v<U&&, T> is false. diff --git a/include/commata/field_scanners.hpp b/include/commata/field_scanners.hpp index 5c97d69..3fca0da 100644 --- a/include/commata/field_scanners.hpp +++ b/include/commata/field_scanners.hpp @@ -235,6 +235,7 @@ public: template + && !std::is_convertible_v && !(std::is_base_of_v> || std::is_base_of_v> || std::is_base_of_v>)>* @@ -244,6 +245,19 @@ public: store_(generic_args_t(), std::forward(u)) {} + template + && std::is_convertible_v + && !(std::is_base_of_v> + || std::is_base_of_v> + || std::is_base_of_v>)>* + = nullptr> + replace_if_skipped(U&& u = std::decay_t()) + noexcept(std::is_nothrow_constructible_v) : + store_(generic_args_t(), std::forward(u)) + {} + explicit replace_if_skipped(replacement_fail_t) noexcept : store_(detail::replace_mode::fail) {} diff --git a/include/commata/text_value_translation.hpp b/include/commata/text_value_translation.hpp index e1705b4..da8d648 100644 --- a/include/commata/text_value_translation.hpp +++ b/include/commata/text_value_translation.hpp @@ -830,11 +830,19 @@ template struct base : base_base { template >* = nullptr> + std::enable_if_t + && is_acceptable_arg_v>* = nullptr> explicit base(const All& for_all = All()) : base(for_all, for_all, for_all) {} + template + && is_acceptable_arg_v>* = nullptr> + base(const All& for_all = All()) : + base(for_all, for_all, for_all) + {} + template @@ -869,11 +877,19 @@ template struct base : base_base { template >* = nullptr> + std::enable_if_t + && is_acceptable_arg_v>* = nullptr> explicit base(const All& for_all = All()) : base(for_all, for_all, for_all, for_all) {} + template + && is_acceptable_arg_v>* = nullptr> + base(const All& for_all = All()) : + base(for_all, for_all, for_all, for_all) + {} + template @@ -925,11 +941,19 @@ template struct base : base_base { template >* = nullptr> + std::enable_if_t + && is_acceptable_arg_v>* = nullptr> explicit base(const All& for_all = All()) : base(for_all, for_all, for_all, for_all, for_all) {} + template + && is_acceptable_arg_v>* = nullptr> + base(const All& for_all = All()) : + base(for_all, for_all, for_all, for_all, for_all) + {} + template diff --git a/src_test/TestTableScanner.cpp b/src_test/TestTableScanner.cpp index 9d988df..da6fa0f 100644 --- a/src_test/TestTableScanner.cpp +++ b/src_test/TestTableScanner.cpp @@ -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>); +static_assert(!std::is_convertible_v>); + +} // end unnamed + struct TestReplaceIfSkipped : BaseTest {}; diff --git a/src_test/TestTextValueTranslation.cpp b/src_test/TestTextValueTranslation.cpp index ed24665..842aefd 100644 --- a/src_test/TestTextValueTranslation.cpp +++ b/src_test/TestTextValueTranslation.cpp @@ -164,6 +164,21 @@ namespace { using ReplacedTypes = testing::Types; +struct B +{}; + +struct D : B +{}; + +struct E +{ + explicit E(const B&) + {} +}; + +static_assert(std::is_convertible_v>); +static_assert(!std::is_convertible_v>); + } // end unnamed TYPED_TEST_SUITE(TestReplaceIfConversionFailed, ReplacedTypes);