Skip to content

Instantly share code, notes, and snippets.

@LYP951018
Created October 24, 2015 02:31
Show Gist options
  • Select an option

  • Save LYP951018/364866a69e2ec20a6761 to your computer and use it in GitHub Desktop.

Select an option

Save LYP951018/364866a69e2ec20a6761 to your computer and use it in GitHub Desktop.
#include <type_traits>
#include <utility>
namespace Yupei
{
//http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4387.html
namespace Internal
{
template<bool...B>
struct StaticAnd;
template<bool B, bool... Args>
struct StaticAnd<B,Args...> : std::__bool_constant<B && StaticAnd<Args...>::value>
{
};
template<>
struct StaticAnd<> : std::true_type
{
};
}
template<typename T>
struct remove_reference
{
using type = T;
};
template<typename T>
struct remove_reference<T&>
{
using type = T;
};
template<typename T>
struct remove_reference<T&&>
{
using type = T;
};
template<typename T>
using remove_reference_t = typename remove_reference<T>::type;
template<typename T>
inline constexpr T&& forward(remove_reference_t<T>& value)
{
//lvalue
return static_cast<T&&>(value);
}
template<typename T>
inline constexpr T&& forward(remove_reference_t<T>&& value)
{
//rvalue
return static_cast<T&&>(value);
}
template<typename T>
constexpr remove_reference_t<T>&& move(T&& value)
{
return static_cast<remove_reference_t<T>&&>(value);
}
template<bool TestVal,
typename Type = void>
struct enable_if
{
};
template<typename Type>
struct enable_if <true, Type>
{
using type = Type;
};
template<bool B, typename Type = void>
using enable_if_t = typename enable_if<B, Type>::type;
template<typename Type, typename... Args>
using is_nothrow_constructible = std::is_nothrow_constructible<Type, Args...>;
/*bool_constant<
__is_nothrow_constructible(Type, Args...)>;*/
template<typename Type>
using is_nothrow_default_constructible = is_nothrow_constructible<
Type
>;
template<typename Type>
using is_nothrow_copy_constructible = is_nothrow_constructible<
Type,
const Type&
>;
template<typename Type>
using is_nothrow_move_constructible = is_nothrow_constructible<
Type,
Type&&
>;
namespace Internal
{
/*template<typename T, typename U, typename = void>
struct IsAssignableHelper : false_type
{
};
template<typename T, typename U>
struct IsAssignableHelper<T, U, void_t<
decltype(Yupei::declval<T>() = Yupei::declval<U>())>> : true_type
{
};*/
struct WrapInt
{
WrapInt(int){}
};
template<typename T,typename U>
struct IsAssignableHelper
{
template<typename T1,typename U1>
static std::false_type _Foo(WrapInt);
template<typename T1, typename U1>
static auto _Foo(int)->
decltype(
(void)(std::declval<T1>() = std::declval<U1>()), std::true_type{});
using type = decltype(_Foo<T,U>(0));
};
}
template<typename T, typename U>
struct is_assignable : Internal::IsAssignableHelper<T, U>::type
{
};
template<typename T, typename U>
using is_nothrow_assignable = std::__bool_constant<
is_assignable<T,U>::value && noexcept(std::declval<T>() = std::declval<U>())
>;
template<typename Type>
using is_nothrow_copy_assignable = is_nothrow_assignable<
Type&,
const Type&
>;
template<typename Type>
using is_nothrow_move_assignable = is_nothrow_assignable<
Type&,
Type&&
>;
template<typename Type>
using is_nothrow_copy_assignable = is_nothrow_assignable<
Type&,
const Type&
>;
template<typename Type>
using is_nothrow_move_assignable = is_nothrow_assignable<
Type&,
Type&&
>;
template<typename Type, typename... Args>
struct is_constructible : std::is_constructible<Type,Args...>{};
template<typename Type>
using is_default_constructible = is_constructible<Type>;
template<typename Type>
using is_copy_constructible = is_constructible<
Type, const Type&>;
template<typename Type>
using is_move_constructible = is_constructible<
Type, Type&&>;
template <typename F,typename T>
using is_convertible = std::is_convertible<F,T>;
struct tuple_alloc_t {};
constexpr tuple_alloc_t tuple_alloc{};
template<typename T1,
typename T2>
struct pair;
template<typename... Args>
class tuple;
template<std::size_t Index,
typename T,
bool B = std::is_empty<T>::value && std::is_final<T>::value>
class tuple_leaf;
template<std::size_t Index,
typename T,
bool B>
class tuple_leaf //cannot EBO
{
public:
tuple_leaf() noexcept(
is_nothrow_default_constructible<T>::value)
:value_()
{
}
//why we need support allocator ?
template<typename U,
typename = enable_if_t<!std::is_same<std::decay_t<U>,tuple_leaf>::value &&
is_constructible<T,U>::value>>
explicit tuple_leaf(U&& u) noexcept(
is_nothrow_constructible<T, U>::value)
:value_(Yupei::forward<U>(u))
{
}
tuple_leaf (const tuple_leaf&) = default;
tuple_leaf(tuple_leaf&&) = default;
template<typename U>
tuple_leaf& operator=(U&& u) noexcept(
is_nothrow_assignable<T&,U>::value)
{
value_ = Yupei::forward<U>(u);
}
int swap(tuple_leaf& rhs) noexcept
{
using std::swap;
swap(value_, rhs.value_);
return 0;
}
T& get() noexcept
{
return value_;
}
const T& get() const noexcept
{
return value_;
}
private:
T value_;
};
template<std::size_t Index,
typename T>
class tuple_leaf<Index,T,true> : private T //EBO enabled.
{
public:
using BaseType = T;
tuple_leaf() noexcept(
is_nothrow_default_constructible<T>::value)
{
}
template<typename U,
typename = enable_if_t<!std::is_same<std::decay_t<U>, tuple_leaf>::value &&
is_constructible<T, U>::value >>
explicit tuple_leaf(U&& u) noexcept(
is_nothrow_constructible<T, U>::value)
:BaseType(Yupei::forward<U>(u))
{
}
tuple_leaf(const tuple_leaf&) = default;
tuple_leaf(tuple_leaf&&) = default;
template<typename U>
tuple_leaf& operator=(U&& u) noexcept(
is_nothrow_assignable<T&, U>::value)
{
BaseType::operator = (Yupei::forward<U>(u));
}
int swap(tuple_leaf& rhs) noexcept
{
using std::swap;
swap(get(), rhs.get());
return 0;
}
T& get() noexcept
{
return static_cast<T&>(*this);
}
const T& get() const noexcept
{
return static_cast<const T&>(*this);
}
};
template<typename... Args>
void swallow(Args&&...) noexcept {}
struct ignore_t
{
template<typename U>
const ignore_t& operator =(U&& u) const noexcept
{
return *this;
}
};
const ignore_t ignore{};
template<typename... Args>
struct tuple_types {};
template<std::size_t Index,typename TupleT>
class tuple_element;
template<std::size_t Index,
typename TupleT>
struct tuple_element<Index, const TupleT>
{
using type = typename tuple_element<Index, TupleT>::type;
};
template<std::size_t Index,
typename TupleT>
struct tuple_element<Index, volatile TupleT>
{
using type = typename tuple_element<Index, TupleT>::type;
};
template<std::size_t Index,
typename TupleT>
struct tuple_element<Index, const volatile TupleT>
{
using type = typename tuple_element<Index, TupleT>::type;
};
template<std::size_t Index>
struct tuple_element<Index, tuple_types<>>
{
static_assert(Index == 0, "tuple_element index out of range.");
static_assert(Index != 0, "tuple_element index out of range.");
};
template<typename T,
typename... Args>
struct tuple_element<0, tuple_types<T, Args...>>
{
using type = T;
};
template<std::size_t Index,
typename T,
typename... Args>
struct tuple_element<Index, tuple_types<T, Args...>>
{
using type = typename tuple_element<Index - 1, tuple_types<Args...>>::type;
};
template<typename... Args>
class tuple;
template<std::size_t Index,typename... Args>
struct tuple_element<Index, tuple<Args...>>
{
using type = typename tuple_element<Index, tuple_types<Args...>>::type;
};
template<std::size_t Index,
typename TupleT>
using tuple_element_t = typename tuple_element<Index, TupleT>::type;
template<std::size_t Index,
typename... Args>
tuple_element_t<Index, tuple<Args...>>& get(tuple<Args...>&) noexcept;
template<std::size_t Index,
typename... Args>
const tuple_element_t<Index, tuple<Args...>>& get(const tuple<Args...>&) noexcept;
template<std::size_t Index,
typename... Args>
tuple_element_t<Index, tuple<Args...>>&& get(tuple<Args...>&&) noexcept;
namespace Internal
{
template<typename ResTupleT, typename SourceTupleT, std::size_t Start, std::size_t End>
struct make_tuple_types_impl;
template<typename... Args,typename SourceTupleT,std::size_t End>
struct make_tuple_types_impl<tuple_types<Args...>, SourceTupleT, End, End>
{
using type = tuple_types<Args...>;
};
template<typename... Args,typename SourceTupleT,std::size_t Start,std::size_t End>
struct make_tuple_types_impl<tuple_types<Args...>, SourceTupleT, Start, End>
{
using RTupleT = remove_reference_t<SourceTupleT>;
using type = typename make_tuple_types_impl<
tuple_types<Args..., tuple_element_t<Start, SourceTupleT>>, SourceTupleT, Start + 1, End>::type;
};
template<typename T>
class tuple_size;
template<typename... Args>
class tuple_size<tuple<Args...>> : public std::integral_constant<std::size_t,sizeof...(Args)> {};
template< typename T>
class tuple_size<const T> : public std::integral_constant<std::size_t, tuple_size<T>::value> {};
template< typename T >
class tuple_size<volatile T> : public std::integral_constant<std::size_t, tuple_size<T>::value> {};
template< typename T >
class tuple_size<const volatile T> : public std::integral_constant<std::size_t, tuple_size<T>::value> {};
template<typename SourceTupleT, std::size_t Start = 0, std::size_t End = tuple_size<SourceTupleT>::value>
struct make_tuple_types
{
static_assert(Start <= End, "Start > End");
using type = typename make_tuple_types_impl<tuple_types<>, SourceTupleT, Start, End>::type;
};
template<typename From, typename To>
struct tuple_convertible : std::false_type {};
template<typename T,
typename... Args,
typename U,
typename... UArgs>
struct tuple_convertible<tuple_types<T, Args...>, tuple_types<U, UArgs...>>
: std::__bool_constant<is_convertible<T, U>::value && tuple_convertible<tuple_types<Args...>, tuple_types<UArgs...>>::value> {};
template<>
struct tuple_convertible<tuple_types<>, tuple_types<>> : std::true_type {};
template<typename, typename>
struct tuple_constructible : std::false_type {};
template<typename T,
typename... Args,
typename U,
typename... UArgs>
struct tuple_constructible<tuple_types<T, Args...>, tuple_types<U, UArgs...>>
: std::__bool_constant<is_constructible<U, T>::value && tuple_constructible<tuple_types<Args...>, tuple_types<UArgs...>>::value> {};
template<>
struct tuple_constructible<tuple_types<>, tuple_types<>> : std::true_type {};
template<typename, typename>
struct tuple_assignable : std::false_type {};
template<typename T,
typename... Args,
typename U,
typename... UArgs>
struct tuple_assignable<tuple_types<T, Args...>, tuple_types<U, UArgs...>>
: std::__bool_constant<is_assignable<U, T>::value && tuple_assignable<tuple_types<Args...>, tuple_types<UArgs...>>::value> {};
template<>
struct tuple_assignable<tuple_types<>, tuple_types<>> : std::true_type {};
}
template<typename IndexT,typename... Args>
class tuple_impl;
template<std::size_t... Index,
typename... Args>
class tuple_impl<std::index_sequence<Index...>,Args...>
:public tuple_leaf<Index,Args>...
{
public:
constexpr tuple_impl() noexcept(
Internal::StaticAnd<is_nothrow_default_constructible<Args>::value...>::value )
{}
template<typename... ParamsT,
typename = enable_if_t<sizeof...(ParamsT) == sizeof...(Args)>>
tuple_impl(ParamsT&&... params) /*noexcept(
Internal::StaticAnd<is_nothrow_constructible<Args, ParamsT>::value...>::value)*/
:tuple_impl::tuple_impl(std::make_index_sequence<sizeof...(ParamsT)>(),tuple_types<ParamsT...>(),Yupei::forward<ParamsT>(params)...)
{
static_assert(sizeof...(ParamsT) == sizeof...(Args), "hahaha");
}
template<std::size_t... Index1,typename... TArgs,
typename... ParamsT,
typename = enable_if_t<sizeof...(ParamsT) == sizeof...(Args) && sizeof...(Index1) == sizeof...(ParamsT)>>
tuple_impl(std::index_sequence<Index1...>, tuple_types<TArgs...>,
ParamsT&&... params) noexcept(
Internal::StaticAnd<is_nothrow_constructible<TArgs, ParamsT>::value...>::value)
:tuple_leaf<Index1,TArgs>(Yupei::forward<TArgs>(params))...
{
}
template<typename TupleT,
typename = enable_if_t<Internal::tuple_constructible<TupleT,tuple<Args...>>::value>>
tuple_impl(TupleT&& rhs) noexcept(Internal::StaticAnd<is_nothrow_constructible<Args,
tuple_element_t<Index,TupleT>>::value...>::value
)
:tuple_leaf<Index,Args>(Yupei::forward<tuple_element_t<Index,TupleT>>(get<Index>(rhs)))...
{
}
template<typename TupleT,
typename = enable_if_t<
Internal::tuple_assignable<TupleT,tuple<Args...>>::value>>
tuple_impl& operator = (TupleT&& t) noexcept(
Internal::StaticAnd<is_nothrow_assignable<Args&,tuple_element_t<Index,TupleT>>::value...>::value)
{
swallow(tuple_leaf<Index, Args>::operator = (Yupei::forward<tuple_element_t<Index, TupleT>>(get<Index>(t)))...);
}
tuple_impl(const tuple_impl&) = default;
tuple_impl(tuple_impl&&) = default;
tuple_impl& operator=(const tuple_impl& t)
noexcept(Internal::StaticAnd<is_nothrow_copy_assignable<Args>::value...>::value)
{
swallow(tuple_leaf<Index, Args>::operator = (static_cast<const tuple_leaf<Index, Args>&>(t).get())...);
return *this;
}
tuple_impl& operator=(tuple_impl&& t)
noexcept(Internal::StaticAnd<is_nothrow_move_assignable<Args>::value...>::value)
{
swallow(tuple_leaf<Index, Args>::operator = (Yupei::forward<Args>(static_cast<tuple_leaf<Index,Args>&>(t).get()))...);
return *this;
}
void swap(tuple_impl& rhs)
{
swallow(tuple_leaf<Index, Args>::swap(static_cast<tuple_leaf<Index, Args>&>(rhs))...);
}
};
template<typename... Args>
class tuple
{
using BaseType = tuple_impl<std::make_index_sequence<sizeof...(Args)>, Args...>;
BaseType base_;
public:
static constexpr auto tuple_size = sizeof...(Args);
public:
template<std::size_t Index,
typename... UArgs>
friend tuple_element_t<Index, tuple<UArgs...>>& get(tuple<UArgs...>&) noexcept;
template<std::size_t Index,
typename... UArgs>
friend const tuple_element_t<Index, tuple<UArgs...>>& get(const tuple<UArgs...>&) noexcept;
template<std::size_t Index,
typename... UArgs>
friend tuple_element_t<Index, tuple<UArgs...>>&& get(tuple<UArgs...>&&) noexcept;
template<typename = enable_if_t<Internal::StaticAnd<is_default_constructible<Args>::value...>::value>>
constexpr tuple() noexcept(
is_nothrow_default_constructible<BaseType>::value)
{}
template<
typename Dummy = void,
enable_if_t<tuple_size >= 1 &&
std::is_same<Dummy,void>::value
&& Internal::StaticAnd<is_copy_constructible<Args>::value...>::value &&
Internal::StaticAnd<is_convertible<const Args&,Args>::value...>::value,bool> = true>
tuple(const Args&... args) noexcept(
is_nothrow_constructible<BaseType, const Args&...>::value)
:base_(args...)
{
}
template<
typename Dummy = void,
enable_if_t<tuple_size >= 1 &&
std::is_same<Dummy, void>::value
&& Internal::StaticAnd<is_copy_constructible<Args>::value...>::value &&
!Internal::StaticAnd<is_convertible<const Args&, Args>::value...>::value,bool> = false>
explicit tuple(const Args&... args)
noexcept(
is_nothrow_constructible<BaseType, const Args&...>::value)
:base_(args...)
{
}
template<typename... UArgs,
enable_if_t<sizeof...(Args) >= 1 && sizeof...(UArgs) == sizeof...(Args)
&& Internal::StaticAnd<is_constructible<Args,UArgs&&>::value...>::value
&& Internal::StaticAnd<is_convertible<UArgs&&, Args>::value...>::value,bool> = false>
tuple(UArgs&&... args)/*noexcept(
is_nothrow_constructible<BaseType, UArgs&&...>::value)*/
:base_(Yupei::forward<UArgs>(args)...)
{
}
template<typename... UArgs,
enable_if_t<tuple_size >= 1 && sizeof...(UArgs) == tuple_size
&& Internal::StaticAnd<is_constructible<Args, UArgs&&>::value...>::value &&
!Internal::StaticAnd<is_convertible<UArgs&&, Args>::value...>::value,bool> = true>
explicit tuple(UArgs&&... args)
noexcept(
is_nothrow_constructible<BaseType,UArgs&&...>::value)
:base_(Yupei::forward<UArgs>(args)...)
{
}
tuple(const tuple&) = default;
tuple(tuple&&) = default;
template<typename... UArgs,
enable_if_t<Internal::StaticAnd<is_constructible<Args,const UArgs&>::value...>::value&&
sizeof...(UArgs) == tuple_size &&
Internal::StaticAnd<is_convertible<const UArgs&,Args>::value...>::value,bool> = false>
constexpr tuple(const tuple<UArgs...>& rhs)
noexcept(is_nothrow_constructible<BaseType,const tuple<UArgs...>&>::value)
:base_(rhs)
{
}
template<typename... UArgs,
enable_if_t<Internal::StaticAnd<is_constructible<Args, const UArgs&>::value...>::value&&
sizeof...(UArgs) == tuple_size &&
!Internal::StaticAnd<is_convertible<const UArgs&, Args>::value...>::value,bool> = true>
constexpr explicit tuple(const tuple<UArgs...>& rhs)
noexcept(is_nothrow_constructible<BaseType, const tuple<UArgs...>&>::value)
:base_(rhs)
{
}
template<typename... UArgs,
enable_if_t<Internal::StaticAnd<is_constructible<Args, UArgs&&>::value...>::value&&
sizeof...(UArgs) == tuple_size &&
Internal::StaticAnd<is_convertible<UArgs&&, Args>::value...>::value,bool> = false>
constexpr tuple(tuple<UArgs...>&& rhs)
noexcept(is_nothrow_constructible<BaseType, tuple<UArgs...>&&>::value)
:base_(Yupei::move(rhs))
{
}
template<typename... UArgs,
enable_if_t<Internal::StaticAnd<is_constructible<Args, UArgs&&>::value...>::value&&
sizeof...(UArgs) == tuple_size &&
!Internal::StaticAnd<is_convertible<UArgs&&, Args>::value...>::value,bool> = false>
constexpr explicit tuple(tuple<UArgs...>&& rhs)
noexcept(is_nothrow_constructible<BaseType, tuple<UArgs...>&&>::value)
:base_(Yupei::move(rhs))
{
}
void swap(tuple& rhs)
{
base_.swap(rhs.base_);
}
};
template<>
class tuple<>
{
constexpr tuple() noexcept = default;
void swap(tuple&) noexcept {}
};
template<typename... Args>
void swap(tuple<Args...>& lhs, tuple<Args...>& rhs)
{
lhs.swap(rhs);
}
/*template<typename... Args,
typename... UArgs>
constexpr bool operator == (const tuple<Args...>& lhs, const tuple<UArgs...>& rhs)
{
return lhs.Equal(rhs);
}
template<typename... Args,
typename... UArgs>
constexpr bool operator < (const tuple<Args...>& lhs, const tuple<UArgs...>& rhs)
{
return lhs.Less(rhs);
}
template<typename... Args,
typename... UArgs>
constexpr bool operator != (const tuple<Args...>& lhs, const tuple<UArgs...>& rhs)
{
return !lhs.Equal(rhs);
}
template<typename... Args,
typename... UArgs>
constexpr bool operator > (const tuple<Args...>& lhs, const tuple<UArgs...>& rhs)
{
return rhs.Less(lhs);
}
template<typename... Args,
typename... UArgs>
constexpr bool operator <= (const tuple<Args...>& lhs, const tuple<UArgs...>& rhs)
{
return !rhs.Less(lhs);
}
template<typename... Args,
typename... UArgs>
constexpr bool operator >= (const tuple<Args...>& lhs, const tuple<UArgs...>& rhs)
{
return !lhs.Less(rhs);
}*/
template< std::size_t I, typename... Args,typename = enable_if_t< (I < sizeof...(Args)) > >
inline constexpr tuple_element_t<I, tuple<Args...> >& get(tuple<Args...>&t)
{
return static_cast<tuple_leaf<I, tuple_element_t<I, tuple<Args...>>>&>(t.base_).get();
}
template< std::size_t I, typename... Args, typename = enable_if_t<( I < sizeof...(Args))> >
inline constexpr const tuple_element_t<I, tuple<Args...> >& get(const tuple<Args...>& t)
{
return static_cast<const tuple_leaf<I, tuple_element_t<I, tuple<Args...>>>&>(t.base_).get();
}
template< std::size_t I, typename... Args, typename = enable_if_t< (I < sizeof...(Args)) > >
inline constexpr tuple_element_t<I, tuple<Args...> >&& get(tuple<Args...>&& t)
{
return static_cast<tuple_leaf<I, tuple_element_t<I, tuple<Args...>>>&&>(t.base_).get();
}
template<typename... Args>
inline constexpr tuple<Args&...> tie(Args&... t) noexcept
{
return tuple<Args&...>(t...);
}
namespace Internal
{
template<typename TypeToFind,
typename Type,
std::size_t NowIndex>
struct get_tuple_by_type_checker;
template<typename TypeToFind,
typename Type,
std::size_t NowIndex>
struct get_tuple_by_type_impl;
template<typename TypeToFind,
typename Type
>
struct get_tuple_by_type;
template<typename TypeToFind,
typename Type,
typename... Args,
std::size_t NowIndex>
struct get_tuple_by_type_impl < TypeToFind, tuple<Type, Args...>,NowIndex>
{
static constexpr std::size_t value = std::conditional_t <
std::is_same<TypeToFind, Type>::value,
get_tuple_by_type_checker<TypeToFind,tuple<Args...>,NowIndex >,//find it,check.
get_tuple_by_type_impl < TypeToFind, tuple<Args...>, NowIndex + 1 >
>::value;
};
template<typename TypeToFind,
std::size_t NowIndex>
struct get_tuple_by_type_impl < TypeToFind, tuple<>, NowIndex>
{
static constexpr std::size_t value = -1;
};
template<typename TypeToFind,
typename... Args,
std::size_t NowIndex>
struct get_tuple_by_type_checker<TypeToFind,tuple<Args...>,NowIndex>
{
static_assert(get_tuple_by_type_impl<TypeToFind, tuple<Args...>, 0>::value == -1, "tuple get<>() type duplicate");
static constexpr std::size_t value = NowIndex;
};
template<typename TypeToFind,
typename... Args
>
struct get_tuple_by_type<TypeToFind, tuple<Args...>>
{
static constexpr std::size_t value = get_tuple_by_type_impl<
TypeToFind,
tuple<Args...>,
0>::value;
static_assert(value != -1, "type not found.");
};
}
template< typename Type, typename... Args>
inline constexpr decltype(auto) get(tuple<Args...>&& t) noexcept
{
return Yupei::get<Internal::get_tuple_by_type<Type, tuple<Args...>>::value >(Yupei::forward<tuple<Args...>&&>(t));
}
template< typename Type, typename... Args >
inline constexpr decltype(auto) get(tuple<Args...>& t) noexcept
{
return Yupei::get<Internal::get_tuple_by_type<Type, tuple<Args...>>::value >(t);
}
template< typename Type, typename... Args >
inline constexpr decltype(auto) get(const tuple<Args...>& t) noexcept
{
return Yupei::get<Internal::get_tuple_by_type<Type, tuple<Args...>>::value >(t);
}
template<class... Args>
inline constexpr tuple<Args&&...> forward_as_tuple(Args&&... t) noexcept
{
return tuple<Args&&...>(Yupei::forward<Args>(t)...);
}
}
int main()
{
Yupei::tuple<int,int,int,int,int> a(1,3,4,5,6);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment