4052. Bogus requirements for piecewise_linear_distribution

Section: 29.5.9.6.2 [rand.dist.samp.pconst], 29.5.9.6.3 [rand.dist.samp.plinear] Status: New Submitter: Jonathan Wakely Opened: 2024-02-05 Last modified: 2025-10-11

Priority: 4

View all other issues in [rand.dist.samp.pconst].

View all issues with New status.

Discussion:

In the second constructor of 29.5.9.6.3 [rand.dist.samp.plinear], P1719R2 replaced:


template<class InputIteratorB, class InputIteratorW>
  piecewise_linear_distribution(InputIteratorB firstB, InputIteratorB lastB,
                                InputIteratorW firstW);

Effects: [...] Moreover, the id-expressions iterator_traits<InputIteratorB>::value_type and iterator_traits<InputIteratorW>::value_type shall each denote a type that is convertible to double.

with

template<class InputIteratorB, class InputIteratorW>
  piecewise_linear_distribution(InputIteratorB firstB, InputIteratorB lastB,
                                InputIteratorW firstW);

Mandates: is_invocable_r_v<double, UnaryOperation&, double> is true.

Preconditions: [...]

Effects: [...]

This was a copy & paste error from the next constructor, and was obviously not intended. There is no UnaryOperation in that constructor.

A less obviously wrong issue is the use of double there in the first place. Shouldn't it be RealType instead? That seems to be incorrect throughout both 29.5.9.6.2 [rand.dist.samp.pconst] and 29.5.9.6.3 [rand.dist.samp.plinear], and was only partially fixed by LWG 1439.

Finally, the preconditions also say:

Preconditions: [...] If firstB == lastB or ++firstB == lastB, let n = 1, ρ0 = ρ1 = 1, b0 = 0, and b1 = 1. Otherwise, [firstB, lastB) forms a sequence b of length n + 1, the length of the sequence w starting from firstW is at least n, and any wk for kn are ignored by the distribution.

These are not preconditions. I think it is trying to say something like:

Preconditions: [...] [firstB, lastB) is a valid range and [firstW, firstW + (lastB - firstB)) is a valid range.

Effects: If firstB == lastB or ++firstB == lastB, let n = 1, ρ0 = ρ1 = 1, b0 = 0, and b1 = 1. Otherwise, let [firstB, lastB) form a sequence b0, …, bn, and let wk = *firstW++ for k = 0, …, n.

The equivalent constructor for piecewise_constant_distribution has similar problems with its preconditions in terms of n + 1.

[2024-03-12; Reflector poll]

Set priority to 4 after reflector poll. The copy & paste error was fixed editorially.

[2025-10-06; Hewill comments and provides wording]

We should fix the constructor, otherwise the following should be rejected according to the standard:

#include <random>

struct Op {
  float operator()(float);
  void operator()(auto) = delete;
};

static_assert(!std::is_invocable_r_v<double, Op&, double>);

int main() {
  std::initializer_list<float> l;
  std::piecewise_linear_distribution<float> dist(l, Op{});
}

This is, because it violates Mandates. However, all three compilers accept it because Op& can take float and return float.

Proposed resolution:

This wording is relative to N5014.

  1. Modify 29.5.9.6.2 [rand.dist.samp.pconst] as indicated:

    template<class InputIteratorB, class InputIteratorW>
      piecewise_constant_distribution(InputIteratorB firstB, InputIteratorB lastB,
                                      InputIteratorW firstW);
    

    -4- Mandates: Both of

    1. (4.1) — is_convertible_v<iterator_traits<InputIteratorB>::value_type, result_typedouble>

    2. (4.2) — is_convertible_v<iterator_traits<InputIteratorW>::value_type, result_typedouble>

    are true.

    […]

    template<class UnaryOperation>
      piecewise_constant_distribution(initializer_list<RealType> bl, UnaryOperation fw);
    

    -7- Mandates: is_invocable_r_v<result_typedouble, UnaryOperation&, result_typedouble> is true.

    […]

    template<class UnaryOperation>
      piecewise_constant_distribution(size_t nw, RealType xmin, RealType xmax, UnaryOperation fw);
    

    -10- Mandates: is_invocable_r_v<result_typedouble, UnaryOperation&, result_typedouble> is true.

    […]

  2. Modify 29.5.9.6.3 [rand.dist.samp.plinear] as indicated:

    template<class InputIteratorB, class InputIteratorW>
      piecewise_linear_distribution(InputIteratorB firstB, InputIteratorB lastB,
                                    InputIteratorW firstW);
    

    -4- Mandates: Both of

    1. (4.1) — is_convertible_v<iterator_traits<InputIteratorB>::value_type, result_typedouble>

    2. (4.2) — is_convertible_v<iterator_traits<InputIteratorW>::value_type, result_typedouble>

    are true.

    […]

    template<class UnaryOperation>
      piecewise_linear_distribution(initializer_list<RealType> bl, UnaryOperation fw);
    

    -7- Mandates: is_invocable_r_v<result_typedouble, UnaryOperation&, result_typedouble> is true.

    […]

    template<class UnaryOperation>
      piecewise_linear_distribution(size_t nw, RealType xmin, RealType xmax, UnaryOperation fw);
    

    -10- Mandates: is_invocable_r_v<result_typedouble, UnaryOperation&, result_typedouble> is true.

    […]