Skip to content

Instantly share code, notes, and snippets.

@EricWF
Created July 10, 2015 23:14
Show Gist options
  • Select an option

  • Save EricWF/dc34df961cd51cce5a30 to your computer and use it in GitHub Desktop.

Select an option

Save EricWF/dc34df961cd51cce5a30 to your computer and use it in GitHub Desktop.
Tuple Bug
#include <type_traits>
#include <tuple>
#include <cassert>
template <bool Pred, class Then = bool>
using EnableIf = typename std::enable_if<Pred, Then>::type;
template <bool Pred>
using BoolT = std::integral_constant<bool, Pred>;
template <class ...Args>
using LazyAnd =
#ifdef _LIBCPP_VERSION
std::__lazy_and<Args...>;
#else
std::__and_<Args...>;
#endif
/**
* MyTupleImp - A class that provides some of the std::tuple constructors.
* If UseExplicit is true then the Tuple(Uargs&&...) is explicit otherwise
* it is implicit.
*/
template <bool UseExplicit, class ...Args>
struct MyTupleImp {
///////////////////////////////////////////////////////////////////////////
// Explicit C++11 Args Converting Constructor
template <class ...UArgs,
EnableIf<LazyAnd<
BoolT<UseExplicit>,
std::is_convertible<UArgs, Args>...
>::value> = false>
explicit
MyTupleImp(UArgs&&...) : LastCalled(ConvertArgs) {}
///////////////////////////////////////////////////////////////////////////
// Implicit C++17 Args Converting Constructor
template <class ...UArgs,
EnableIf<LazyAnd<
BoolT<!UseExplicit>,
std::is_convertible<UArgs, Args>...
>::value> = false>
MyTupleImp(UArgs&&...) : LastCalled(ConvertArgs) {}
///////////////////////////////////////////////////////////////////////////
// C++11 and C++17 Tuple Converting Constructor
template <class ...UArgs,
class = EnableIf<LazyAnd<
std::is_convertible<UArgs, Args>...
>::value>>
MyTupleImp(MyTupleImp<UseExplicit, UArgs...>&&) : LastCalled(ConvertTuple) {}
enum CtorCalled {
ConvertArgs,
ConvertTuple
};
int LastCalled;
};
// Provides the C++11 explicit Tuple(UArgs&&...) constructor.
template <class ...Args>
using Tuple11 = MyTupleImp<true, Args...>;
// Provides the C++17 implicit Tuple(UArgs&&...) constructor.
template <class ...Args>
using Tuple17 = MyTupleImp<false, Args...>;
int main() {
{ // C++11 Tuple Passes.
using Tuple = Tuple11<int>;
using NestedTuple = Tuple11< Tuple11<int>&& >;
Tuple t(42);
NestedTuple t2(std::move(t));
assert(t2.LastCalled == NestedTuple::ConvertArgs);
}
{ // C++17 Tuple FAILS!
using Tuple = Tuple17<int>;
using NestedTuple = Tuple17< Tuple17<int>&& >;
Tuple t(42);
NestedTuple t2(std::move(t));
assert(t2.LastCalled == NestedTuple::ConvertArgs);
// Tuple(TupleLike&&) is called with
// with Tuple = Tuple17< Tuple17<int>&& >
// TupleLike = Tuple17<int>&&
// -- Which calls --
// TupleLeaf< Tuple17<int>&& >( int && )
// -- Which constructs --
// Tuple17<int>&& Leaf from int &&
// Leaf is constructed from a temporary Tuple<int>.
// The temporary goes out of scope immediately.
// --------------------
// NestedTuple now contains a dangling reference.
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment