tuple/variadic: split into type/value/pack operations
This commit is contained in:
parent
f3f3666877
commit
12d401b98b
@ -368,8 +368,8 @@ list (
|
||||
time.hpp
|
||||
time/parse.hpp
|
||||
time/parse8601.cpp
|
||||
tuple.cpp
|
||||
tuple.hpp
|
||||
tuple/type.hpp
|
||||
tuple/value.hpp
|
||||
typeidx.cpp
|
||||
typeidx.hpp
|
||||
types/bits.hpp
|
||||
|
@ -22,10 +22,9 @@
|
||||
|
||||
// we specifically rely on vector<bool> to compute a few logical operations
|
||||
#include "../vector.hpp"
|
||||
|
||||
#include "../tuple/value.hpp"
|
||||
#include "../debug.hpp"
|
||||
#include "../maths.hpp"
|
||||
#include "../tuple.hpp"
|
||||
#include "../preprocessor.hpp"
|
||||
#include "../types/bits.hpp"
|
||||
|
||||
@ -495,7 +494,7 @@ namespace util {
|
||||
return RetT {
|
||||
std::apply (
|
||||
func,
|
||||
::util::tuple::convert (
|
||||
::util::tuple::value::map (
|
||||
static_cast<
|
||||
const value_t& (&)(const part_t&)
|
||||
> (
|
||||
|
@ -420,7 +420,7 @@ namespace util {
|
||||
izip (const ContainerT&... data)
|
||||
{
|
||||
return zip (
|
||||
::util::make_indices (::util::variadic::first (data...)),
|
||||
::util::make_indices (::util::variadic::get<0> (data...)),
|
||||
data...
|
||||
);
|
||||
}
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
#include "../pool.hpp"
|
||||
|
||||
#include "../tuple.hpp"
|
||||
|
||||
#include "ticketlock.hpp"
|
||||
#include "semaphore.hpp"
|
||||
#include "flag.hpp"
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "tuple.hpp"
|
||||
#include "tuple/type.hpp"
|
||||
#include "tuple/value.hpp"
|
||||
|
||||
#include "tap.hpp"
|
||||
|
||||
@ -17,12 +18,19 @@ main ()
|
||||
{
|
||||
util::TAP::logger tap;
|
||||
|
||||
static_assert (
|
||||
util::tuple::type::index<
|
||||
std::tuple<float,int,void>,
|
||||
int
|
||||
>::value == 1
|
||||
);
|
||||
|
||||
{
|
||||
auto tuple = std::make_tuple (1,2,3,4);
|
||||
std::vector<int> expected {{ 1, 2, 3, 4 }};
|
||||
|
||||
std::vector<int> actual;
|
||||
util::tuple::for_each ([&actual] (auto i) { actual.push_back (i); }, tuple);
|
||||
util::tuple::value::each ([&actual] (auto i) { actual.push_back (i); }, tuple);
|
||||
|
||||
tap.expect_eq (actual, expected, "value iteration");
|
||||
}
|
||||
@ -38,7 +46,7 @@ main ()
|
||||
};
|
||||
|
||||
std::vector<std::type_index> actual;
|
||||
util::tuple::for_type<decltype(tuple)> ([&actual] (auto i) {
|
||||
util::tuple::type::each<decltype(tuple)> ([&actual] (auto i) {
|
||||
actual.push_back (typeid (typename decltype(i)::type));
|
||||
});
|
||||
|
||||
@ -51,7 +59,7 @@ main ()
|
||||
|
||||
{
|
||||
using src_t = std::tuple<std::string>;
|
||||
using dst_t = typename util::tuple::map<src_t, int_mapper>::type;
|
||||
using dst_t = typename util::tuple::type::map<src_t, int_mapper>::type;
|
||||
|
||||
tap.expect (std::is_same<dst_t, std::tuple<int>>::value, "tuple type mapping");
|
||||
}
|
||||
|
19
tuple.cpp
19
tuple.cpp
@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "tuple.hpp"
|
||||
|
||||
// Make sure _someone_ includes the header for syntax checking
|
229
tuple.hpp
229
tuple.hpp
@ -1,229 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_TUPLE_HPP
|
||||
#define __UTIL_TUPLE_HPP
|
||||
|
||||
#include "types.hpp"
|
||||
#include "variadic.hpp"
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
|
||||
|
||||
namespace util::tuple {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// call a provided object with type_tag<T> for each type in a tuple
|
||||
template <
|
||||
typename T,
|
||||
typename F,
|
||||
size_t S = 0
|
||||
>
|
||||
typename std::enable_if<
|
||||
S == std::tuple_size<T>::value,
|
||||
void
|
||||
>::type
|
||||
for_type (F)
|
||||
{ }
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
template<
|
||||
typename T,
|
||||
typename F,
|
||||
size_t S = 0
|
||||
>
|
||||
typename std::enable_if<
|
||||
S < std::tuple_size<T>::value,
|
||||
void
|
||||
>::type
|
||||
for_type (F f)
|
||||
{
|
||||
using E = typename std::tuple_element<S,T>::type;
|
||||
|
||||
f (type_tag<E> {});
|
||||
for_type<T,F,S+1> (f);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
template <typename T, typename F>
|
||||
auto for_type (F f, T t)
|
||||
{ return for_type<decltype(t)> (f); }
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// call a provided object with each value in a tuple
|
||||
template <
|
||||
size_t S = 0,
|
||||
typename F,
|
||||
typename ...T
|
||||
>
|
||||
typename std::enable_if<
|
||||
S == sizeof...(T),
|
||||
void
|
||||
>::type
|
||||
for_each (F, const std::tuple<T...>&)
|
||||
{ }
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
template<
|
||||
size_t S = 0,
|
||||
typename F,
|
||||
typename ...T
|
||||
>
|
||||
typename std::enable_if<
|
||||
S < sizeof...(T),
|
||||
void
|
||||
>::type
|
||||
for_each (F f, const std::tuple<T...> &t)
|
||||
{
|
||||
f (std::get<S> (t));
|
||||
for_each<S+1,F,T...> (f, t);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// Statically map the member types of a tuple via F<>::type
|
||||
///
|
||||
/// T: tuple type
|
||||
/// F: type mapping object, conversion uses F<>::type
|
||||
/// I: tuple indexing helper
|
||||
template <
|
||||
typename T,
|
||||
template <
|
||||
typename
|
||||
> class F,
|
||||
typename I = std::make_index_sequence<
|
||||
std::tuple_size<T>::value
|
||||
>
|
||||
>
|
||||
struct map;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <
|
||||
typename T,
|
||||
template <
|
||||
typename
|
||||
> class F,
|
||||
size_t ...I
|
||||
>
|
||||
struct map<
|
||||
T,
|
||||
F,
|
||||
std::index_sequence<I...>
|
||||
> {
|
||||
typedef std::tuple<
|
||||
typename F<
|
||||
typename std::tuple_element<I, T>::type
|
||||
>::type...
|
||||
> type;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
namespace detail {
|
||||
template <typename FuncT, typename ArgT, std::size_t ...Indices>
|
||||
auto
|
||||
convert (std::index_sequence<Indices...>, FuncT &&func, ArgT &&arg)
|
||||
{
|
||||
return std::tuple (std::invoke (func, std::get<Indices> (arg))...);
|
||||
}
|
||||
};
|
||||
|
||||
template <
|
||||
typename FuncT,
|
||||
typename ArgT,
|
||||
typename IndicesV = std::make_index_sequence<
|
||||
std::tuple_size_v<std::decay_t<ArgT>>
|
||||
>
|
||||
>
|
||||
auto convert (FuncT &&func, ArgT &&arg)
|
||||
{
|
||||
return detail::convert (IndicesV{}, func, arg);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// do nothing with a set of parameters.
|
||||
///
|
||||
/// useful for temporarily silencing unused argument warnings in parameter
|
||||
/// packs, or for avoiding assignment of [[gnu::warn_unused_result]] to a
|
||||
/// temporary value we'd just cast to void anyway (GCC#66425).
|
||||
///
|
||||
/// it is guaranteed that this function will never be defined out in
|
||||
/// debug/release/whatever builds. so it is safe to use to guarantee
|
||||
/// parameter evaluation.
|
||||
inline void
|
||||
ignore (void) noexcept
|
||||
{ ; }
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
template <typename T, typename ...Args>
|
||||
void
|
||||
ignore (T, const Args&...) noexcept
|
||||
{ ; }
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// query the index of the first occurrence of type `T' in the tuple type
|
||||
/// `TupleT'.
|
||||
///
|
||||
/// if the query type does not occur in the tuple type a compiler error
|
||||
/// should be generated.
|
||||
template <class T, class TupleT>
|
||||
struct index;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
template <class T, class ...Types>
|
||||
struct index<T, std::tuple<T, Types...>> {
|
||||
static constexpr std::size_t value = 0;
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
template <class T, class U, class ...Types>
|
||||
struct index<T,std::tuple<U, Types...>> {
|
||||
static constexpr std::size_t value = 1 + index<T, std::tuple<Types...>>::value;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
template <size_t IndexV, typename HeadT, typename ...TailT>
|
||||
auto
|
||||
get (HeadT &&head, TailT &&...tail)
|
||||
{
|
||||
if constexpr (IndexV == 0)
|
||||
return std::forward<HeadT> (head);
|
||||
else
|
||||
return get<IndexV-1> (std::forward<TailT> (tail)...);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
template <std::size_t Idx, typename ...Args>
|
||||
auto
|
||||
nth (Args &&...args)
|
||||
{
|
||||
return std::get<Idx> (std::tuple {std::forward<Args> (args)...});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
162
tuple/type.hpp
Normal file
162
tuple/type.hpp
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef CRUFT_UTIL_TUPLE_TYPE_HPP
|
||||
#define CRUFT_UTIL_TUPLE_TYPE_HPP
|
||||
|
||||
#include "../types.hpp"
|
||||
|
||||
#include "../types/traits.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
|
||||
|
||||
namespace util::tuple::type {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// call a provided FunctorT with type_tag<T> for each type in the
|
||||
/// tuple-like type TupleT.
|
||||
template<
|
||||
typename TupleT,
|
||||
typename FunctorT,
|
||||
size_t S = 0
|
||||
>
|
||||
void
|
||||
each (FunctorT &&func)
|
||||
{
|
||||
static_assert (S < std::tuple_size_v<TupleT>);
|
||||
using value_type = typename std::tuple_element<S,TupleT>::type;
|
||||
|
||||
func (util::type_tag<value_type> {});
|
||||
|
||||
if constexpr (S + 1 < std::tuple_size_v<TupleT>) {
|
||||
each<TupleT,FunctorT,S+1> (std::forward<FunctorT> (func));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// Statically map the member types of a tuple via F<>::type
|
||||
///
|
||||
/// TupleT: tuple type
|
||||
/// FieldT: type mapping object, conversion uses FieldT<>::type
|
||||
/// Indices: tuple indexing helper
|
||||
template <
|
||||
typename TupleT,
|
||||
template <
|
||||
typename
|
||||
> class FieldT,
|
||||
typename Indices = std::make_index_sequence<
|
||||
std::tuple_size<TupleT>::value
|
||||
>
|
||||
>
|
||||
struct map;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <
|
||||
typename TupleT,
|
||||
template <
|
||||
typename
|
||||
> class FieldT,
|
||||
size_t ...Indices
|
||||
>
|
||||
struct map<
|
||||
TupleT,
|
||||
FieldT,
|
||||
std::index_sequence<Indices...>
|
||||
> {
|
||||
typedef std::tuple<
|
||||
typename FieldT<
|
||||
typename std::tuple_element<Indices, TupleT>::type
|
||||
>::type...
|
||||
> type;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// query the index of the first occurrence of type `T' in the tuple-like
|
||||
/// type `TupleT'.
|
||||
///
|
||||
/// if the query type does not occur in the tuple type a compiler error
|
||||
/// should be generated.
|
||||
template<
|
||||
typename TupleT,
|
||||
typename ValueT,
|
||||
typename = std::make_index_sequence<
|
||||
std::tuple_size_v<TupleT>
|
||||
>
|
||||
>
|
||||
struct index { };
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// specialisation for tuples with matching first elements. the index is 0.
|
||||
template<
|
||||
typename ValueT,
|
||||
typename ...TailT,
|
||||
size_t ...Indices
|
||||
>
|
||||
struct index<
|
||||
std::tuple<ValueT, TailT...>,
|
||||
ValueT,
|
||||
std::index_sequence<Indices...>
|
||||
> {
|
||||
static constexpr std::size_t value = 0;
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// specialisation for tuples with non-matching first elements.
|
||||
// increment and recurse.
|
||||
template<
|
||||
typename ValueT,
|
||||
typename HeadT,
|
||||
typename ...TailT,
|
||||
size_t ...Indices
|
||||
>
|
||||
struct index<
|
||||
std::tuple<HeadT, TailT...>,
|
||||
ValueT,
|
||||
std::index_sequence<Indices...>
|
||||
> {
|
||||
static constexpr std::size_t value = 1u + index<std::tuple<TailT...>,ValueT>::value;
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// convert the tuple type (which is not a tuple or it would have matched
|
||||
// the tuple specialisation, into the equivalent std::tuple and requery
|
||||
// against that.
|
||||
template <
|
||||
typename TupleT,
|
||||
typename ValueT,
|
||||
size_t ...Indices
|
||||
> struct index<
|
||||
TupleT,
|
||||
ValueT,
|
||||
std::index_sequence<Indices...>
|
||||
> {
|
||||
static constexpr std::size_t value = index<
|
||||
std::tuple<std::tuple_element_t<Indices,TupleT>...>,
|
||||
ValueT,
|
||||
std::index_sequence<Indices...>
|
||||
>::value;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif
|
75
tuple/value.hpp
Normal file
75
tuple/value.hpp
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef CRUFT_UTIL_TUPLE_VALUE_HPP
|
||||
#define CRUFT_UTIL_TUPLE_VALUE_HPP
|
||||
|
||||
#include "../types.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace util::tuple::value {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Call a provided functor of type FunctionT with each value in a
|
||||
/// provided tuple-like object TupleT
|
||||
template<
|
||||
typename FunctionT,
|
||||
typename TupleT,
|
||||
std::size_t S = 0
|
||||
>
|
||||
void
|
||||
each (FunctionT &&func, TupleT &&value)
|
||||
{
|
||||
using tuple_t = std::decay_t<TupleT>;
|
||||
static_assert (S < std::tuple_size_v<tuple_t>);
|
||||
|
||||
func (std::get<S> (value));
|
||||
|
||||
if constexpr (S + 1 < std::tuple_size_v<tuple_t>) {
|
||||
each<FunctionT,TupleT,S+1> (std::forward<FunctionT> (func), std::forward<TupleT> (value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
namespace detail {
|
||||
template <typename FuncT, typename ArgT, std::size_t ...Indices>
|
||||
auto
|
||||
map (std::index_sequence<Indices...>, FuncT &&func, ArgT &&arg)
|
||||
{
|
||||
return std::tuple (std::invoke (func, std::get<Indices> (arg))...);
|
||||
}
|
||||
};
|
||||
|
||||
template <
|
||||
typename FuncT,
|
||||
typename ArgT,
|
||||
typename IndicesV = std::make_index_sequence<
|
||||
std::tuple_size_v<std::decay_t<ArgT>>
|
||||
>
|
||||
>
|
||||
auto map (FuncT &&func, ArgT &&arg)
|
||||
{
|
||||
return detail::map (IndicesV{}, func, arg);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -14,13 +14,14 @@
|
||||
* Copyright 2011-2014 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_TYPES_HPP
|
||||
#define __UTIL_TYPES_HPP
|
||||
#ifndef CRUFT_UTIL_TYPES_HPP
|
||||
#define CRUFT_UTIL_TYPES_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
50
variadic.hpp
50
variadic.hpp
@ -11,25 +11,49 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
* Copyright 2017-2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_VARIADIC_HPP
|
||||
#define __UTIL_VARIADIC_HPP
|
||||
#ifndef CRUFT_UTIL_VARIADIC_HPP
|
||||
#define CRUFT_UTIL_VARIADIC_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace util::variadic {
|
||||
/// return a reference to the first item in a parameter pack
|
||||
template <typename T, typename ...Tail>
|
||||
T&
|
||||
first (T &t, Tail&&...)
|
||||
{ return t; }
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// do nothing with a set of parameters.
|
||||
///
|
||||
/// useful for temporarily silencing unused argument warnings in parameter
|
||||
/// packs, or for avoiding assignment of [[gnu::warn_unused_result]] to a
|
||||
/// temporary value we'd just cast to void anyway (GCC#66425).
|
||||
///
|
||||
/// it is guaranteed that this function will never be defined out in
|
||||
/// debug/release/whatever builds. so it is safe to use to guarantee
|
||||
/// parameter evaluation.
|
||||
template <typename ...Args>
|
||||
void
|
||||
ignore (const Args&...) noexcept ((std::is_nothrow_destructible_v<Args> && ...))
|
||||
{ ; }
|
||||
|
||||
|
||||
/// return a reference to the first item in a const parameter pack
|
||||
template <typename T, typename ...Tail>
|
||||
const T&
|
||||
first (const T &t, const Tail&&...)
|
||||
{ return t; }
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Returns the argument at index `IndexV', as if we called:
|
||||
/// std::get<N> (std::make_tuple (...))
|
||||
template <std::size_t IndexV, typename HeadT, typename ...TailT>
|
||||
auto
|
||||
get (HeadT &&head, TailT &&...tail) noexcept ((std::is_nothrow_move_constructible_v<TailT> && ...))
|
||||
{
|
||||
static_assert (IndexV < sizeof... (TailT) + 1, "Index is out of bounds");
|
||||
|
||||
if constexpr (IndexV == 0)
|
||||
return std::forward<HeadT> (head);
|
||||
else
|
||||
return get<IndexV-1> (std::forward<TailT> (tail)...);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user