This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

4230. simd<complex>::real/imag is overconstrained

Section: 29.10.7.4 [simd.complex.access] Status: New Submitter: Matthias Kretz Opened: 2025-03-18 Last modified: 2025-03-22

Priority: Not Prioritized

View all issues with New status.

Discussion:

29.10.7.4 [simd.complex.access] overconstrains the arguments to real and imag. complex<T>::real/imag allows conversions to T whereas simd<complex<T>> requires basically an exact match (same_as<simd<T>> modulo ABI tag differences).

complex<double> c = {};
c.real(1.f); // OK

simd<complex<double>> sc = {};
sc.real(simd<float>(1.f)); // ill-formed, should be allowed

The design intent was to match the std::complex<T> interface. In which case the current wording doesn't match that intent. complex doesn't say real(same_as<T> auto) but 'real(T)', which allows conversions.

This issue is also present in the basic_simd(real, imag) constructor. It deduces the type for the real/imag arguments instead of using a dependent type derived from value_type and ABI tag.

// OK:
complex<double> c{1., 1.f};

// Ill-formed, should be allowed:
simd<complex<double>> sc0(1., 1.);
simd<complex<double>, 4> sc1(simd<double, 4>(1.), simd<float, 4>(1.f));

Proposed resolution:

This wording is relative to N5008.

  1. Modify 29.10.6.1 [simd.overview], class template basic_simd synopsis, as indicated:

    namespace std::datapar {
      template<class T, class Abi> class basic_simd {
      public:
        using value_type = T;
        using mask_type = basic_simd_mask<sizeof(T), Abi>;
        using abi_type = Abi;
        
        using real-type = rebind_t<typename T::value_type, basic_simd> // exposition-only
        
        // 29.10.6.2 [simd.ctor], basic_simd constructors   
        […]
        template<simd-floating-point V>
          constexpr explicit(see below) basic_simd(const real-typeV& reals, const real-typeV& imags = {}) noexcept;
        […]
        // 29.10.7.4 [simd.complex.access], basic_simd complex-value accessors
        constexpr real-typeauto real() const noexcept;
        constexpr real-typeauto imag() const noexcept;
        template<simd-floating-point V>
          constexpr void real(const real-typeV& v) noexcept;
        template<simd-floating-point V>
          constexpr void imag(const real-typeV& v) noexcept;
        […]
      };
      […]
    }
    
  2. Modify 29.10.6.2 [simd.ctor] as indicated:

    template<simd-floating-point V>
      constexpr explicit(see below) basic_simd(const real-typeV& reals, const real-typeV& imags = {}) noexcept;
    

    -19- Constraints:

    1. (19.1) — simd-complex<basic_simd> is modeled., and

    2. (19.2) — V::size() == size() is true.

    […]

    -21- Remarks: The expression inside explicit evaluates to false if and only if the floating-point conversion rank of T::value_type is greater than or equal to the floating-point conversion rank of real-typeV::value_type.

  3. Modify 29.10.7.4 [simd.complex.access] as indicated:

    constexpr real-typeauto real() const noexcept;
    constexpr real-typeauto imag() const noexcept;
    

    -1- Constraints: simd-complex<basic_simd> is modeled.

    -2- Returns: An object of type real-typerebind_t<typename T::value_type, basic_simd> where the ith element is initialized to the result of cmplx-func(operator[](i)) for all i in the range [0, size()), where cmplx-func is the corresponding function from <complex>.

    template<simd-floating-point V>
      constexpr void real(const real-typeV& v) noexcept;
    template<simd-floating-point V>
      constexpr void imag(const real-typeV& v) noexcept;
    

    -3- Constraints:

    1. (3.1) — simd-complex<basic_simd> is modeled.,

    2. (3.2) — same_as<typename V::value_type, typename T::value_type> is modeled, and

    3. (3.3) — V::size() == size() is true.

    […]