[cfe-dev] Breaking change: Removing std::tuple's "reduced-arity-initialization" extension on uses-allocator constructors.

Eric Fiselier via cfe-dev cfe-dev at lists.llvm.org
Thu Apr 14 20:36:24 PDT 2016


The extension was removed in r266409.

On Mon, Apr 11, 2016 at 9:29 PM, Eric Fiselier <eric at efcs.ca> wrote:

> Hi All,
>
> I will be removing the "reduced-arity-initialization" extension from the
> uses-allocator constructors in std::tuple. This is a breaking change. I
> plan to make this change immediately without first deprecating the behavior.
>
> I'm hoping that usage of this extension with uses-allocator construction
> is rare and this change will cause minimal breakage.
>
> This will not affect the extension in non-uses-allocator cases. Nor will
> it affect conforming uses-allocator construction.  This change is needed to
> fix a number of bugs and to ensure future changes in tuple don't cause more
> breakage.
>
> Please let me know if you have any objections.
>
> Below is a more in-depth description of the problem and solution.
>
> ==========
> The Problem
> ==========
>
> Libc++'s tuple provides an extension where a tuple can be created using
> fewer initializers than elements in the tuple. The remaining elements are
> simply default initialized. Example:
>
> ```
> std::tuple<int, float, std::string> t1(42, 0.f); // #1 default constructs
> string.
> std::tuple<int, float, std::string> t2(std::allocator_arg,
> std::allocator<void>{}); // #2 default constructs int, float and string.
> ```
>
> However this optimization causes the uses-allocator overloads to
> participate in overload resolution when they otherwise shouldn't. In
> example #1 Clang considers and substitutes into std::tuple(allocator_arg_t,
> Alloc const&, Up&&...) with [Alloc = float, Up = []].
>
> This can poison the entire overload set by causing the evaluation of
> SFINAE not permitted by the standard. For example the following code blows
> up while substituting into the uses-allocator constructors during the
> evaluation of std::is_default_constructible<UPtr>.
>
> ```
> void DeleteFn(int*);
> using UPtr = std::unique_ptr<int, void(*)(int*)>;
> std::tuple<UPtr, UPtr> t(UPtr(nullptr, &DeleteFn), UPtr(nullptr,
> &DeleteFn));  // triggers a static_assert in std::unique_ptr's default ctor.
> ```
>
> ========
> Resolution
> ========
>
> The problem is fixed by removing the extension from the uses-allocator
> constructors. By doing this we can immediately disambiguate the
> uses-allocator  and regular constructors based only on the arity of the
> call. This can be used to prevent non-standard evaluation of "dangerous"
> SFINAE traits like "is_constructible" and "is_convertible".
>
> Note that the extension will continue to be offered for the regular
> non-uses-allocator constructors.
>
> ======================
> Other Considered Resolution
> ======================
>
> The other solution I considered was to change the 'allocator_arg_t'
> parameter to be deduced and
> adding SFINAE to check if the deduced type is convertible to
> 'allocator_arg_t'. This approach has a number of issues:
>
> 1. Regular overloads would also have add additional SFINAE to check if
> their parameter pack started with an object that was convertible to
> allocator_arg_t. This check could break existing code.
> 2. Deducing the allocator_arg_t parameter can cause slight semantic
> differences compared to using a concrete type.
> 3. This prevents 'allocator_arg_t' from being stored in a tuple.
>
> Thanks
> /Eric
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20160414/eb79ae07/attachment.html>


More information about the cfe-dev mailing list