Boost logo

Boost :

Subject: Re: [boost] Boost 1.61.0 Release Candidate 1
From: Stephan T. Lavavej (stl_at_[hidden])
Date: 2016-05-06 16:38:51

[Frank Mori Hess]
> There is a testcase3.cpp attached to ticket 12110
> You have to uncomment the foo call.

Ok, this one's different from the repros I saw earlier.

Here, VS 2015 Update 2's implementation is correctly implementing the C++17 Working Paper N4582. What's happening is that you have a DoubleHolder with an unconstrained constructor DoubleHolder(const ValueType&). (It really should be constrained to exist only when its body would compile.) Then you construct a tuple<DoubleHolder>. Finally, you pass it to foo() which (after template argument deduction) takes a tuple<DoubleHolder> by value.

What is INTENDED is copy construction, from the modifiable lvalue x. There are two other relevant constructors, though. The converting copy constructor turns out to be a red herring - it doesn't participate because of LWG 2549's PR, and even if it did, the plain copy ctor would be preferred (const tuple& beats const tuple<U>&). The one that wins is the perfect forwarding ctor.

The change here is that the perfect forwarding ctor is (sometimes) implicit in C++17, to support brace-returning tuples. In this case, it is indeed implicit because DoubleHolder's constructor is implicit. So, it has to be considered when constructing foo()'s parameter from x in main(). This perfect forwarding constructor participates because DoubleHolder claims to be able to accept a tuple<DoubleHolder> (that's the unconstrained bit previously mentioned). And the perfect forwarding ctor wins because it takes (tuple<DoubleHolder>&), which is an exact match and better than the copy ctor's (const tuple<DoubleHolder>&).

This is similar to the other bug report that I mentioned, except that your DoubleHolder avoids additional weirdness caused by the UDT constructor taking by value. The root cause is the same: tuple's perfect forwarding ctor can out-compete the copy ctor.

I've been informed that libc++, while having recently implemented the WP Standardese, is immune to this issue because they implemented a (non-Standard) constraint to prevent the perfect forwarding ctor from outcompeting here. And I've been informed that libstdc++'s implementation of the WP is equally affected like VC's, although I'm not sure how new of a version (perhaps trunk) is required.

I am strongly considering applying libc++'s strategy to VC for Update 3. In general, I am wary of doing anything not mandated by the Standard, especially in something as complicated as tuple, but the disambiguation should be simple to implement and very targeted (I cannot imagine a reason where the perfect forwarding ctor should ever be allowed to outcompete the copy/move ctor).

In the meantime, I thought of an additional workaround for Boost. Instead of using 2-tuples which are immune, if you detect when this scenario is about to happen (i.e. trying to copy this kind of 1-tuple), if you manually get<0> the element within, that should avoid the problem of giving whole tuples to types like DoubleHolder.


Boost list run by bdawes at, gregod at, cpdaniel at, john at