[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
Mon Apr 11 20:29:41 PDT 2016


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/20160411/8e5c694e/attachment.html>


More information about the cfe-dev mailing list