2746. Inconsistency between requirements for emplace between optional and variant

Section: 22.5.3.4 [optional.assign], 22.6.3.5 [variant.mod], 22.7.4.4 [any.modifiers] Status: New Submitter: Richard Smith Opened: 2016-07-13 Last modified: 2020-05-10

Priority: 3

View all issues with New status.

Discussion:

Referring to N4604:

In [optional.object.assign]: emplace (normal form) has a Requires that the construction works.

Requires: is_constructible_v<T, Args&&...> is true.

emplace (initializer_list form) has a SFINAE condition:

Remarks: […] unless is_constructible_v<T, initializer_list<U>&, Args&&...> is true.

In 22.7.4.4 [any.modifiers]: emplace (normal form) has a Requires that the construction works:

Requires: is_constructible_v<T, Args...> is true.

emplace (initializer_list form) has a SFINAE condition:

Remarks: […] unless is_constructible_v<T, initializer_list<U>&, Args...> is true.

In 22.6.3.5 [variant.mod]: emplace (T, normal form) has a SFINAE condition:

Remarks: […] unless is_constructible_v<T, Args...> is true, and T occurs exactly once in Types....

emplace (Idx, normal form) has a both a Requires and a SFINAE condition:

Requires: I < sizeof...(Types)

Remarks: […] unless is_constructible_v<T, Args...> is true, and T occurs exactly once in Types....

emplace (T, initializer_list form) has a SFINAE condition:

Remarks: […] unless is_constructible_v<T, initializer_list<U>&, Args...> is true, and T occurs exactly once in Types....

emplace (Idx, initializer_list form) has a both a Requires and a SFINAE condition:

Requires: I < sizeof...(Types)

Remarks: […] unless is_constructible_v<T, Args...> is true, and T occurs exactly once in Types....

Why the inconsistency? Should all the cases have a SFINAE requirement?

I see that variant has an additional requirement (T occurs exactly once in Types...), but that only agues that it must be a SFINAE condition — doesn't say that the other cases (any/variant) should not.

map/multimap/unordered_map/unordered_multimap have SFINAE'd versions of emplace that don't take initializer_lists, but they don't have any emplace versions that take ILs.

Suggested resolution:

Add SFINAE requirements to optional::emplace(Args&&... args) and any::emplace(Args&&... args);

[2016-08 Chicago]

During issue prioritization, people suggested that this might apply to any as well.

Ville notes that 2746, 2754 and 2756 all go together.

[2020-05-10; Daniel comments and provides wording]

The inconsistency between the two any::emplace overloads have been removed by resolving LWG 2754 to use Constraints: elements. The last Mandating paper (P1460R1), adopted in Prague, changed the Requires: elements for variant::emplace, "I < sizeof...(Types)" to Mandates:, but that paper was focused on fixing inappropriate preconditions, not searching for consistency here. Given that the in_place_index_t constructors of variant uses SFINAE-conditions for this form of static precondition violation, I recommend that its emplace functions use the same style, which would bring them also in consistency with their corresponding type-based emplace forms that are Mandates:-free but delegate to the index-based forms.

Proposed resolution:

This wording is relative to N4861.

  1. Modify 22.5.3.4 [optional.assign], as indicated:

    template<class... Args> T& emplace(Args&&... args);
    

    -29- MandatesConstraints: is_constructible_v<T, Args...> is true.

    […]

    template<class U, class... Args> T& emplace(initializer_list<U> il, Args&&... args);
    

    -35- Constraints: is_constructible_v<T, initializer_list<U>&, Args...> is true.

    […]

  2. Modify 22.6.3.5 [variant.mod], as indicated:

    template<class T, class... Args> T& emplace(Args&&... args);
    

    -1- Constraints: is_constructible_v<T, Args...> is true, and T occurs exactly once in Types.

    […]

    template<class T, class U, class... Args> T& emplace(initializer_list<U> il, Args&&... args);
    

    -3- Constraints: is_constructible_v<T, initializer_list<U>&, Args...> is true, and T occurs exactly once in Types.

    […]

    template<size_t I, class... Args>
      variant_alternative_t<I, variant<Types...>>& emplace(Args&&... args);
    

    -5- Mandates: I < sizeof...(Types).

    -6- Constraints: is_constructible_v<TI, Args...> is true and I < sizeof...(Types) is true.

    […]

    template<size_t I, class U, class... Args>
      variant_alternative_t<I, variant<Types...>>& emplace(initializer_list<U> il, Args&&... args);
    

    -12- Mandates: I < sizeof...(Types).

    -13- Constraints: is_constructible_v<TI, initializer_list<U>&, Args...> is true and I < sizeof...(Types) is true.

    […]