2015-05-21 19:33:49 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
/* A variadic tuple class. */
|
|
|
|
|
|
|
|
#ifndef mozilla_Tuple_h
|
|
|
|
#define mozilla_Tuple_h
|
|
|
|
|
|
|
|
#include "mozilla/Move.h"
|
2015-07-23 15:42:00 -07:00
|
|
|
#include "mozilla/Pair.h"
|
2015-05-21 19:33:49 -07:00
|
|
|
#include "mozilla/TemplateLib.h"
|
|
|
|
#include "mozilla/TypeTraits.h"
|
|
|
|
|
|
|
|
#include <stddef.h>
|
2015-07-23 15:42:00 -07:00
|
|
|
#include <utility>
|
2015-05-21 19:33:49 -07:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A helper class that allows passing around multiple variadic argument lists
|
|
|
|
* by grouping them.
|
|
|
|
*/
|
|
|
|
template<typename... Ts>
|
|
|
|
struct Group;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CheckConvertibility checks whether each type in a source pack of types
|
|
|
|
* is convertible to the corresponding type in a target pack of types.
|
|
|
|
*
|
|
|
|
* It is intended to be invoked like this:
|
|
|
|
* CheckConvertibility<Group<SourceTypes...>, Group<TargetTypes...>>
|
|
|
|
* 'Group' is used to separate types in the two packs (otherwise if we just
|
|
|
|
* wrote 'CheckConvertibility<SourceTypes..., TargetTypes...', it couldn't
|
|
|
|
* know where the first pack ends and the second begins).
|
|
|
|
*
|
|
|
|
* Note that we need to check explicitly that the two packs are of the same
|
|
|
|
* size, because attempting to simultaneously expand two parameter packs
|
|
|
|
* is an error (and it would be a hard error, because it wouldn't be in the
|
|
|
|
* immediate context of the caller).
|
|
|
|
*/
|
|
|
|
|
|
|
|
template<typename Source, typename Target, bool SameSize>
|
|
|
|
struct CheckConvertibilityImpl;
|
|
|
|
|
|
|
|
template<typename Source, typename Target>
|
|
|
|
struct CheckConvertibilityImpl<Source, Target, false>
|
|
|
|
: FalseType {};
|
|
|
|
|
|
|
|
template<typename... SourceTypes, typename... TargetTypes>
|
|
|
|
struct CheckConvertibilityImpl<Group<SourceTypes...>, Group<TargetTypes...>, true>
|
|
|
|
: IntegralConstant<bool, tl::And<IsConvertible<SourceTypes, TargetTypes>::value...>::value> { };
|
|
|
|
|
|
|
|
template<typename Source, typename Target>
|
|
|
|
struct CheckConvertibility;
|
|
|
|
|
|
|
|
template<typename... SourceTypes, typename... TargetTypes>
|
|
|
|
struct CheckConvertibility<Group<SourceTypes...>, Group<TargetTypes...>>
|
|
|
|
: CheckConvertibilityImpl<Group<SourceTypes...>, Group<TargetTypes...>,
|
|
|
|
sizeof...(SourceTypes) == sizeof...(TargetTypes)> { };
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TupleImpl is a helper class used to implement mozilla::Tuple.
|
|
|
|
* It represents one node in a recursive inheritance hierarchy.
|
|
|
|
* 'Index' is the 0-based index of the tuple element stored in this node;
|
|
|
|
* 'Elements...' are the types of the elements stored in this node and its
|
|
|
|
* base classes.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* Tuple<int, float, char> inherits from
|
|
|
|
* TupleImpl<0, int, float, char>, which stores the 'int' and inherits from
|
|
|
|
* TupleImpl<1, float, char>, which stores the 'float' and inherits from
|
|
|
|
* TupleImpl<2, char>, which stores the 'char' and inherits from
|
|
|
|
* TupleImpl<3>, which stores nothing and terminates the recursion.
|
|
|
|
*
|
|
|
|
* The purpose of the 'Index' parameter is to allow efficient index-based
|
|
|
|
* access to a tuple element: given a tuple, and an index 'I' that we wish to
|
|
|
|
* access, we can cast the tuple to the base which stores the I'th element
|
|
|
|
* by performing template argument deduction against 'TupleImpl<I, E...>',
|
|
|
|
* where 'I' is specified explicitly and 'E...' is deduced (this is what the
|
|
|
|
* non-member 'Get<N>(t)' function does).
|
|
|
|
*
|
|
|
|
* This implementation strategy is borrowed from libstdc++'s std::tuple
|
|
|
|
* implementation.
|
|
|
|
*/
|
|
|
|
template<std::size_t Index, typename... Elements>
|
|
|
|
struct TupleImpl;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The base case of the inheritance recursion (and also the implementation
|
|
|
|
* of an empty tuple).
|
|
|
|
*/
|
|
|
|
template<std::size_t Index>
|
|
|
|
struct TupleImpl<Index> {};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* One node of the recursive inheritance hierarchy. It stores the element at
|
|
|
|
* index 'Index' of a tuple, of type 'HeadT', and inherits from the nodes
|
|
|
|
* that store the remaining elements, of types 'TailT...'.
|
|
|
|
*/
|
|
|
|
template<std::size_t Index, typename HeadT, typename... TailT>
|
|
|
|
struct TupleImpl<Index, HeadT, TailT...>
|
|
|
|
: public TupleImpl<Index + 1, TailT...>
|
|
|
|
{
|
|
|
|
typedef TupleImpl<Index + 1, TailT...> Base;
|
|
|
|
|
|
|
|
// Accessors for the head and the tail.
|
|
|
|
// These are static, because the intended usage is for the caller to,
|
|
|
|
// given a tuple, obtain the type B of the base class which stores the
|
|
|
|
// element of interest, and then call B::Head(tuple) to access it.
|
|
|
|
// (Tail() is mostly for internal use, but is exposed for consistency.)
|
|
|
|
static HeadT& Head(TupleImpl& aTuple) { return aTuple.mHead; }
|
|
|
|
static const HeadT& Head(const TupleImpl& aTuple) { return aTuple.mHead; }
|
|
|
|
static Base& Tail(TupleImpl& aTuple) { return aTuple; }
|
|
|
|
static const Base& Tail(const TupleImpl& aTuple) { return aTuple; }
|
|
|
|
|
|
|
|
TupleImpl() : Base(), mHead() { }
|
|
|
|
|
|
|
|
// Construct from const references to the elements.
|
|
|
|
explicit TupleImpl(const HeadT& aHead, const TailT&... aTail)
|
|
|
|
: Base(aTail...), mHead(aHead) { }
|
|
|
|
|
|
|
|
// Construct from objects that are convertible to the elements.
|
|
|
|
// This constructor is enabled only when the argument types are actually
|
|
|
|
// convertible to the element types, otherwise it could become a better
|
|
|
|
// match for certain invocations than the copy constructor.
|
|
|
|
template <typename OtherHeadT, typename... OtherTailT,
|
|
|
|
typename = typename EnableIf<
|
|
|
|
CheckConvertibility<
|
|
|
|
Group<OtherHeadT, OtherTailT...>,
|
|
|
|
Group<HeadT, TailT...>>::value>::Type>
|
|
|
|
explicit TupleImpl(OtherHeadT&& aHead, OtherTailT&&... aTail)
|
|
|
|
: Base(Forward<OtherTailT>(aTail)...), mHead(Forward<OtherHeadT>(aHead)) { }
|
|
|
|
|
|
|
|
// Copy and move constructors.
|
|
|
|
// We'd like to use '= default' to implement these, but MSVC 2013's support
|
|
|
|
// for '= default' is incomplete and this doesn't work.
|
|
|
|
TupleImpl(const TupleImpl& aOther)
|
|
|
|
: Base(Tail(aOther))
|
|
|
|
, mHead(Head(aOther)) {}
|
|
|
|
TupleImpl(TupleImpl&& aOther)
|
|
|
|
: Base(Move(Tail(aOther)))
|
2015-07-18 00:48:39 -07:00
|
|
|
, mHead(Forward<HeadT>(Head(aOther))) {}
|
|
|
|
|
|
|
|
// Assign from a tuple whose elements are convertible to the elements
|
|
|
|
// of this tuple.
|
|
|
|
template <typename... OtherElements,
|
|
|
|
typename = typename EnableIf<
|
|
|
|
sizeof...(OtherElements) == sizeof...(TailT) + 1>::Type>
|
|
|
|
TupleImpl& operator=(const TupleImpl<Index, OtherElements...>& aOther)
|
|
|
|
{
|
|
|
|
typedef TupleImpl<Index, OtherElements...> OtherT;
|
|
|
|
Head(*this) = OtherT::Head(aOther);
|
|
|
|
Tail(*this) = OtherT::Tail(aOther);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
template <typename... OtherElements,
|
|
|
|
typename = typename EnableIf<
|
|
|
|
sizeof...(OtherElements) == sizeof...(TailT) + 1>::Type>
|
|
|
|
TupleImpl& operator=(TupleImpl<Index, OtherElements...>&& aOther)
|
|
|
|
{
|
|
|
|
typedef TupleImpl<Index, OtherElements...> OtherT;
|
|
|
|
Head(*this) = Move(OtherT::Head(aOther));
|
|
|
|
Tail(*this) = Move(OtherT::Tail(aOther));
|
|
|
|
return *this;
|
|
|
|
}
|
2015-05-21 19:33:49 -07:00
|
|
|
|
|
|
|
// Copy and move assignment operators.
|
|
|
|
TupleImpl& operator=(const TupleImpl& aOther)
|
|
|
|
{
|
|
|
|
Head(*this) = Head(aOther);
|
|
|
|
Tail(*this) = Tail(aOther);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
TupleImpl& operator=(TupleImpl&& aOther)
|
|
|
|
{
|
|
|
|
Head(*this) = Move(Head(aOther));
|
|
|
|
Tail(*this) = Move(Tail(aOther));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
HeadT mHead; // The element stored at this index in the tuple.
|
|
|
|
};
|
|
|
|
|
2015-07-13 08:25:42 -07:00
|
|
|
} // namespace detail
|
2015-05-21 19:33:49 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Tuple is a class that stores zero or more objects, whose types are specified
|
|
|
|
* as template parameters. It can be thought of as a generalization of Pair,
|
|
|
|
* (which can be thought of as a 2-tuple).
|
|
|
|
*
|
|
|
|
* Tuple allows index-based access to its elements (with the index having to be
|
|
|
|
* known at compile time) via the non-member function 'Get<N>(tuple)'.
|
|
|
|
*/
|
|
|
|
template<typename... Elements>
|
|
|
|
class Tuple : public detail::TupleImpl<0, Elements...>
|
|
|
|
{
|
|
|
|
typedef detail::TupleImpl<0, Elements...> Impl;
|
|
|
|
public:
|
|
|
|
// The constructors and assignment operators here are simple wrappers
|
|
|
|
// around those in TupleImpl.
|
|
|
|
|
|
|
|
Tuple() : Impl() { }
|
|
|
|
explicit Tuple(const Elements&... aElements) : Impl(aElements...) { }
|
|
|
|
// Here, we can't just use 'typename... OtherElements' because MSVC will give
|
|
|
|
// a warning "C4520: multiple default constructors specified" (even if no one
|
|
|
|
// actually instantiates the constructor with an empty parameter pack -
|
|
|
|
// that's probably a bug) and we compile with warnings-as-errors.
|
|
|
|
template <typename OtherHead, typename... OtherTail,
|
|
|
|
typename = typename EnableIf<
|
|
|
|
detail::CheckConvertibility<
|
|
|
|
detail::Group<OtherHead, OtherTail...>,
|
|
|
|
detail::Group<Elements...>>::value>::Type>
|
|
|
|
explicit Tuple(OtherHead&& aHead, OtherTail&&... aTail)
|
|
|
|
: Impl(Forward<OtherHead>(aHead), Forward<OtherTail>(aTail)...) { }
|
|
|
|
Tuple(const Tuple& aOther) : Impl(aOther) { }
|
|
|
|
Tuple(Tuple&& aOther) : Impl(Move(aOther)) { }
|
|
|
|
|
2015-07-18 00:48:39 -07:00
|
|
|
template <typename... OtherElements,
|
|
|
|
typename = typename EnableIf<
|
|
|
|
sizeof...(OtherElements) == sizeof...(Elements)>::Type>
|
|
|
|
Tuple& operator=(const Tuple<OtherElements...>& aOther)
|
|
|
|
{
|
|
|
|
static_cast<Impl&>(*this) = aOther;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
template <typename... OtherElements,
|
|
|
|
typename = typename EnableIf<
|
|
|
|
sizeof...(OtherElements) == sizeof...(Elements)>::Type>
|
|
|
|
Tuple& operator=(Tuple<OtherElements...>&& aOther)
|
|
|
|
{
|
|
|
|
static_cast<Impl&>(*this) = Move(aOther);
|
|
|
|
return *this;
|
|
|
|
}
|
2015-05-21 19:33:49 -07:00
|
|
|
Tuple& operator=(const Tuple& aOther)
|
|
|
|
{
|
|
|
|
static_cast<Impl&>(*this) = aOther;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
Tuple& operator=(Tuple&& aOther)
|
|
|
|
{
|
|
|
|
static_cast<Impl&>(*this) = Move(aOther);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-07-23 15:42:00 -07:00
|
|
|
/**
|
|
|
|
* Specialization of Tuple for two elements.
|
|
|
|
* This is created to support construction and assignment from a Pair or std::pair.
|
|
|
|
*/
|
|
|
|
template <typename A, typename B>
|
|
|
|
class Tuple<A, B> : public detail::TupleImpl<0, A, B>
|
|
|
|
{
|
|
|
|
typedef detail::TupleImpl<0, A, B> Impl;
|
|
|
|
|
|
|
|
public:
|
|
|
|
// The constructors and assignment operators here are simple wrappers
|
|
|
|
// around those in TupleImpl.
|
|
|
|
|
|
|
|
Tuple() : Impl() { }
|
|
|
|
explicit Tuple(const A& aA, const B& aB) : Impl(aA, aB) { }
|
|
|
|
template <typename AArg, typename BArg,
|
|
|
|
typename = typename EnableIf<
|
|
|
|
detail::CheckConvertibility<
|
|
|
|
detail::Group<AArg, BArg>,
|
|
|
|
detail::Group<A, B>>::value>::Type>
|
|
|
|
explicit Tuple(AArg&& aA, BArg&& aB)
|
|
|
|
: Impl(Forward<AArg>(aA), Forward<BArg>(aB)) { }
|
|
|
|
Tuple(const Tuple& aOther) : Impl(aOther) { }
|
|
|
|
Tuple(Tuple&& aOther) : Impl(Move(aOther)) { }
|
|
|
|
explicit Tuple(const Pair<A, B>& aOther)
|
|
|
|
: Impl(aOther.first(), aOther.second()) { }
|
|
|
|
explicit Tuple(Pair<A, B>&& aOther) : Impl(Forward<A>(aOther.first()),
|
|
|
|
Forward<B>(aOther.second())) { }
|
|
|
|
explicit Tuple(const std::pair<A, B>& aOther)
|
|
|
|
: Impl(aOther.first, aOther.second) { }
|
|
|
|
explicit Tuple(std::pair<A, B>&& aOther) : Impl(Forward<A>(aOther.first),
|
|
|
|
Forward<B>(aOther.second)) { }
|
|
|
|
|
|
|
|
template <typename AArg, typename BArg>
|
|
|
|
Tuple& operator=(const Tuple<AArg, BArg>& aOther)
|
|
|
|
{
|
|
|
|
static_cast<Impl&>(*this) = aOther;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
template <typename AArg, typename BArg>
|
|
|
|
Tuple& operator=(Tuple<AArg, BArg>&& aOther)
|
|
|
|
{
|
|
|
|
static_cast<Impl&>(*this) = Move(aOther);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
Tuple& operator=(const Tuple& aOther)
|
|
|
|
{
|
|
|
|
static_cast<Impl&>(*this) = aOther;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
Tuple& operator=(Tuple&& aOther)
|
|
|
|
{
|
|
|
|
static_cast<Impl&>(*this) = Move(aOther);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
template <typename AArg, typename BArg>
|
|
|
|
Tuple& operator=(const Pair<AArg, BArg>& aOther)
|
|
|
|
{
|
|
|
|
Impl::Head(*this) = aOther.first();
|
|
|
|
Impl::Tail(*this).Head(*this) = aOther.second();
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
template <typename AArg, typename BArg>
|
|
|
|
Tuple& operator=(Pair<AArg, BArg>&& aOther)
|
|
|
|
{
|
|
|
|
Impl::Head(*this) = Forward<AArg>(aOther.first());
|
|
|
|
Impl::Tail(*this).Head(*this) = Forward<BArg>(aOther.second());
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
template <typename AArg, typename BArg>
|
|
|
|
Tuple& operator=(const std::pair<AArg, BArg>& aOther)
|
|
|
|
{
|
|
|
|
Impl::Head(*this) = aOther.first;
|
|
|
|
Impl::Tail(*this).Head(*this) = aOther.second;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
template <typename AArg, typename BArg>
|
|
|
|
Tuple& operator=(std::pair<AArg, BArg>&& aOther)
|
|
|
|
{
|
|
|
|
Impl::Head(*this) = Forward<AArg>(aOther.first);
|
|
|
|
Impl::Tail(*this).Head(*this) = Forward<BArg>(aOther.second);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-05-21 19:33:49 -07:00
|
|
|
/**
|
|
|
|
* Specialization of Tuple for zero arguments.
|
|
|
|
* This is necessary because if the primary template were instantiated with
|
|
|
|
* an empty parameter pack, the 'Tuple(Elements...)' constructors would
|
|
|
|
* become illegal overloads of the default constructor.
|
|
|
|
*/
|
|
|
|
template <>
|
|
|
|
class Tuple<> {};
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper functions for implementing Get<N>(tuple).
|
|
|
|
* These functions take a TupleImpl<Index, Elements...>, with Index being
|
|
|
|
* explicitly specified, and Elements being deduced. By passing a Tuple
|
|
|
|
* object as argument, template argument deduction will do its magic and
|
|
|
|
* cast the tuple to the base class which stores the element at Index.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Const reference version.
|
|
|
|
template<std::size_t Index, typename... Elements>
|
|
|
|
auto TupleGetHelper(TupleImpl<Index, Elements...>& aTuple)
|
|
|
|
-> decltype(TupleImpl<Index, Elements...>::Head(aTuple))
|
|
|
|
{
|
|
|
|
return TupleImpl<Index, Elements...>::Head(aTuple);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Non-const reference version.
|
|
|
|
template<std::size_t Index, typename... Elements>
|
|
|
|
auto TupleGetHelper(const TupleImpl<Index, Elements...>& aTuple)
|
|
|
|
-> decltype(TupleImpl<Index, Elements...>::Head(aTuple))
|
|
|
|
{
|
|
|
|
return TupleImpl<Index, Elements...>::Head(aTuple);
|
|
|
|
}
|
|
|
|
|
2015-07-13 08:25:42 -07:00
|
|
|
} // namespace detail
|
2015-05-21 19:33:49 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Index-based access to an element of a tuple.
|
|
|
|
* The syntax is Get<Index>(tuple). The index is zero-based.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* Tuple<int, float, char> t;
|
|
|
|
* ...
|
|
|
|
* float f = Get<1>(t);
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Non-const reference version.
|
|
|
|
template<std::size_t Index, typename... Elements>
|
|
|
|
auto Get(Tuple<Elements...>& aTuple)
|
|
|
|
-> decltype(detail::TupleGetHelper<Index>(aTuple))
|
|
|
|
{
|
|
|
|
return detail::TupleGetHelper<Index>(aTuple);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Const reference version.
|
|
|
|
template<std::size_t Index, typename... Elements>
|
|
|
|
auto Get(const Tuple<Elements...>& aTuple)
|
|
|
|
-> decltype(detail::TupleGetHelper<Index>(aTuple))
|
|
|
|
{
|
|
|
|
return detail::TupleGetHelper<Index>(aTuple);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rvalue reference version.
|
|
|
|
template<std::size_t Index, typename... Elements>
|
|
|
|
auto Get(Tuple<Elements...>&& aTuple)
|
|
|
|
-> decltype(Move(mozilla::Get<Index>(aTuple)))
|
|
|
|
{
|
|
|
|
// We need a 'mozilla::' qualification here to avoid
|
|
|
|
// name lookup only finding the current function.
|
|
|
|
return Move(mozilla::Get<Index>(aTuple));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A convenience function for constructing a tuple out of a sequence of
|
|
|
|
* values without specifying the type of the tuple.
|
|
|
|
* The type of the tuple is deduced from the types of its elements.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* auto tuple = MakeTuple(42, 0.5f, 'c'); // has type Tuple<int, float, char>
|
|
|
|
*/
|
|
|
|
template<typename... Elements>
|
2015-11-02 16:50:06 -08:00
|
|
|
inline Tuple<typename Decay<Elements>::Type...>
|
|
|
|
MakeTuple(Elements&&... aElements)
|
2015-05-21 19:33:49 -07:00
|
|
|
{
|
2015-11-02 16:50:06 -08:00
|
|
|
return Tuple<typename Decay<Elements>::Type...>(Forward<Elements>(aElements)...);
|
2015-05-21 19:33:49 -07:00
|
|
|
}
|
|
|
|
|
2015-07-18 00:48:39 -07:00
|
|
|
/**
|
|
|
|
* A convenience function for constructing a tuple of references to a
|
|
|
|
* sequence of variables. Since assignments to the elements of the tuple
|
|
|
|
* "go through" to the referenced variables, this can be used to "unpack"
|
|
|
|
* a tuple into individual variables.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* int i;
|
|
|
|
* float f;
|
|
|
|
* char c;
|
|
|
|
* Tie(i, f, c) = FunctionThatReturnsATuple();
|
|
|
|
*/
|
|
|
|
template<typename... Elements>
|
2015-11-02 16:50:06 -08:00
|
|
|
inline Tuple<Elements&...>
|
|
|
|
Tie(Elements&... aVariables)
|
2015-07-18 00:48:39 -07:00
|
|
|
{
|
|
|
|
return Tuple<Elements&...>(aVariables...);
|
|
|
|
}
|
|
|
|
|
2015-05-21 19:33:49 -07:00
|
|
|
} // namespace mozilla
|
|
|
|
|
|
|
|
#endif /* mozilla_Tuple_h */
|