Consider this function template:
template <class... T>
void foo (std::tuple<T, char, double> ... x);
This invocation works:
using K = std::tuple<int, char, double>;
foo ( K{1,'2',3.0}, K{4,'5',6.0}, K{7,'8',9.0} );
This one doesn't:
foo ( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );
(gcc and clang both complain about too many arguments for foo)
Why is the second call a problem? Can I rewrite the declaration of foo so that the second call is also accepted?
Thee template parameter T is only used to implement variadicity. The actual type is known and fixed, only the number of arguments varies. In real life the types are different from int, char, double, this is just an example.
I cannot use C++17 for this. A C++11-compatible solution is much preferred.
Generate an overloaded set of constructors:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::initializer;
initializer(indexed<T, Is>... ts)
{
// ts is a pack of std::tuple<int, char, double>
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...> {};
using foo = initializer<std::tuple<int, char, double>, 20>;
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO
Generate an overloaded set of function call operators:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::operator();
int operator()(indexed<T, Is>... ts) const
{
// ts is a pack of std::tuple<int, char, double>
return 1;
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...>
{
int operator()() const { return 0; }
};
static constexpr initializer<std::tuple<int, char, double>, 20> foo = {};
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO 2
Create (or generate with preprocessor macros) a set of overloads that forward arguments to a single implementation:
#include <array>
#include <tuple>
using K = std::tuple<int, char, double>;
void foo(const std::array<K*, 5>& a)
{
// a is an array of at most 5 non-null std::tuple<int, char, double>*
}
void foo(K p0) { foo({&p0}); }
void foo(K p0, K p1) { foo({&p0, &p1}); }
void foo(K p0, K p1, K p2) { foo({&p0, &p1, &p2}); }
void foo(K p0, K p1, K p2, K p3) { foo({&p0, &p1, &p2, &p3}); }
void foo(K p0, K p1, K p2, K p3, K p4) { foo({&p0, &p1, &p2, &p3, &p4}); }
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
DEMO 3
Pass as an array and deduce its size (requires additional pair of parens):
#include <tuple>
#include <cstddef>
template <std::size_t N>
void foo(const std::tuple<int, char, double> (&a)[N])
{
// a is an array of exactly N std::tuple<int, char, double>
}
int main()
{
foo({{1,'2',3.0}, {4,'5',6.0}});
// ^~~~~~ extra parens ~~~~~^
}
DEMO 4
Use an std::initializer_list as a constructor parameter (to skip extra parens):
#include <tuple>
#include <initializer_list>
struct foo
{
foo(std::initializer_list<std::tuple<int, char, double>> li)
{
// li is an initializer list of std::tuple<int, char, double>
}
};
int main()
{
foo{ {1,'2',3.0}, {4,'5',6.0} };
}
DEMO 5
{} is not an expression hence don't have type, argument deduction is concerned about types, special care is taken when the argument used to perform argument deduction is an initializer list the template function parameter must have specifics forms, otherwise the parameter is a non-deduced context. A more simplistic example is this:
template <class T> struct A { T r; };
template <class T>
void foo (A<T> x);
using K = A<int>;
foo({1}); // fail
foo(K{1}); // compile
This is covered by [temp.deduc.call]/1
If removing references and cv-qualifiers from P gives std::initializer_list<P'> or P'[N] for some P' and N and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument, and in the P'[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context
and [temp.deduct.type]/5
The non-deduced contexts are:
(5.6) A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).
When you:
explicitly provide template arguments, it works ... nothing to deduce
specify the argument as K{1}, it works ... the argument is not longer an initializer list, is an expression with type.
I cannot use C++17 for this. A C++11-compatible solution is much preferred.
With C++11 is a little more complicated (no std::index_sequence, no std::make_index_sequence) but, if you want maintain the variadic use of tuples... that is... if you substantially want something as
foo (std::tuple<int, char, double> ... ts)
and if you accept to call a static method of a template struct, you can define a template struct that recursively inherit itself and, recursively, define a
func ();
func (K t0);
func (K t0, K t1);
func (K t0, K t1, K t2);
where K is your
using K = std::tuple<int, char, double>;
The following is a full compiling C++11 example
#include <tuple>
#include <iostream>
using K = std::tuple<int, char, double>;
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <int ...>
struct iList;
template <std::size_t = 50u, std::size_t = 0u, typename = iList<>>
struct foo;
template <std::size_t Top, std::size_t N, int ... Is>
struct foo<Top, N, iList<Is...>> : public foo<Top, N+1u, iList<0, Is...>>
{
using foo<Top, N+1u, iList<0, Is...>>::func;
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <std::size_t Top, int ... Is>
struct foo<Top, Top, iList<Is...>>
{
// fake func, for recursion ground case
static void func ()
{ }
};
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
If you can use C++14, you can use std::make_index_sequence and std::index_sequence and the code become a little better, IMHO
#include <tuple>
#include <iostream>
#include <type_traits>
using K = std::tuple<int, char, double>;
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <std::size_t N = 50, typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>> : public foo<N-1u>
{
using foo<N-1u>::func;
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <>
struct foo<0, std::index_sequence<>>
{
static void func ()
{ std::cout << "0" << std::endl; }
};
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
It's a pity you can't use C++17 because in you could use variadic unsing and avoid at all recursive inheritance
#include <tuple>
#include <iostream>
#include <type_traits>
using K = std::tuple<int, char, double>;
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getTypeStruct
{ using type = T; };
template <typename T, std::size_t N>
using getType = typename getTypeStruct<T, N>::type;
template <std::size_t N, typename = IndSeqFrom<N>>
struct bar;
template <std::size_t N, std::size_t ... Is>
struct bar<N, std::index_sequence<Is...>>
{
static void func (getType<K, Is> ... ts)
{ std::cout << sizeof...(ts) << std::endl; }
};
template <std::size_t N = 50, typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>> : public bar<Is>...
{ using bar<Is>::func...; };
int main()
{
foo<>::func({1,'2',3.0}); // print 1
foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
}
Related
I would like to write a class that takes a size N (> 0) and a variable number of arguments (>= N). It should have a constructor that takes N arguments and a member std::tuple which has the same type:
template <size_t N, typename... Args>
struct Example {
// A constructor taking N parameters of type Args[N], initializing the member tuple
// (e.g. param 1 has type Args[0], param 2 has type Args[1], ...,
// param N has type Args[N-1])
// A tuple with N elements, each corresponding to Args[N]
// (e.g. std::tuple<Args[0], ..., Args[N-1]>)
//For instance Example<3, int, float, int, bool> should result in
constexpr Example(int a, float b, int c): t(a, b, c) {}
std::tuple<int, float, int> t;
}
In general: Is this possible? If not are there viable alternatives? Why does/ doesn't this work? I'm using C++20.
To the extent I understand the question, it simply seems to be asking how to produce a tuple from arguments. Which, using Boost.Mp11, is a short one-liner (as always):
template <size_t N, typename... Args>
using Example = mp_take_c<N, std::tuple<Args...>>;
Rather than Example<3, int, float, int, bool> being some type that has a member tuple<int, float, int> with one constructor, it actually is tuple<int, float int>.
If you, for some reason, specifically need exactly a member tuple and exactly the constructor specified, we can do easily enough:
template <typename... Ts>
struct ExampleImpl {
std::tuple<Ts...> t;
constexpr ExampleImpl(Ts... ts) : t(ts...) { }
};
template <size_t N, typename... Args>
using Example = mp_take_c<N, ExampleImpl<Args...>>;
Here is an implementation using C++20:
#include <cstddef>
#include <utility>
#include <tuple>
template <class... Ts> struct typelist;
template <size_t N, class, class> struct take;
// Takes N elements of the given type pack
template <size_t N, class... Ts>
using take_t = typename take<N, typelist<>, typelist<Ts...>>::type;
template <class Take, class Drop>
struct take<0, Take, Drop> {
using type = Take;
};
template <size_t N, class T, class... Ts, class... Us>
requires(N > 0)
struct take<N, typelist<Us...>, typelist<T, Ts...>> {
using type = typename take<N - 1, typelist<Us..., T>, typelist<Ts...>>::type;
};
template <class Ts>
struct make_ctor;
template <class... Ts>
struct make_ctor<typelist<Ts...>> {
constexpr make_ctor(Ts... ts) : tuple(ts...) {}
std::tuple<Ts...> tuple;
};
template <size_t N, class... Args>
struct Example :
make_ctor<take_t<N, Args...>> {
using make_ctor<take_t<N, Args...>>::make_ctor;
};
int main() {
Example<3, int, float, int, bool> e(3, 3.14, 4);
}
What we do here is, first we drop the extra template arguments using the take meta-function. However, since type packs are not first class in C++, we only get to take a typelist of our desired types. To create a tuple + a constructor from a given typelist, we have a helper type called make_ctor. By inheriting its constructor, we get the desired API.
For production, see Barry's answer and use an existing library for the metaprogramming part.
Using std::tuple_element and an helper base class, seems to me that you can write something as follows (C++14 is enough)
#include <tuple>
#include <string>
template <typename ...>
struct ExHelper;
template <std::size_t ... Is, typename Tpl>
struct ExHelper<std::index_sequence<Is...>, Tpl>
{
using t_type = std::tuple<typename std::tuple_element<Is, Tpl>::type...>;
t_type t;
constexpr ExHelper(typename std::tuple_element<Is, Tpl>::type ... args)
: t{std::move(args)...}
{ }
};
template <std::size_t N, typename... Args>
struct Example : public ExHelper<std::make_index_sequence<N>,
std::tuple<Args...>> {
static_assert( N <= sizeof...(Args), "!" );
using ExHelper<std::make_index_sequence<N>,
std::tuple<Args...>>::ExHelper;
};
int main ()
{
Example<3, int, float, int, std::string> e0{1, 2.0f, 3};
static_assert( std::is_same<decltype(e0)::t_type,
std::tuple<int, float, int>>::value, "!" );
}
You could make_index_sequence<N> to help with extracting the first N from Args....
Example:
#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>
// Create a tuple from the sizeof...(I) first elements in the supplied tuple:
template <class... Ts, size_t... I>
constexpr auto extract_from_tuple(std::tuple<Ts...>&& tmp, std::index_sequence<I...>) {
static_assert(sizeof...(Ts) >= sizeof...(I));
// make a new tuple:
return std::make_tuple(std::move(std::get<I>(tmp))...);
}
template <size_t N, class... Args>
struct Example {
template<class... Ts, std::enable_if_t<sizeof...(Ts)==N, int> = 0>
constexpr Example(Ts&&... args)
: t{extract_from_tuple(std::make_tuple(std::forward<Ts>(args)...),
std::make_index_sequence<N>{})} {}
// using the same extract_from_tuple function to deduce the type of `t`
decltype(extract_from_tuple(std::make_tuple(std::declval<Args>()...),
std::make_index_sequence<N>{})) t;
};
int main() {
Example<3, int, float, double, bool> foo(1, 2.f, 3.);
static_assert(std::is_same_v<decltype(foo.t), std::tuple<int, float, double>>);
}
There are so-called Template Deduction Guides. It is marked in code below with comment "// Template Deduction Guide".
This guide shows how to deduce template types of a structure from arguments of constructor.
Now you can call constructor (as I did in main()) with any types.
Try it online!
#include <tuple>
template <std::size_t N, typename ... Args>
struct Example {
constexpr Example(Args && ... args)
: t(std::forward<Args>(args)...) {}
std::tuple<Args...> t;
};
// Template Deduction Guide
template <typename ... Args>
Example(Args && ...) -> Example<sizeof...(Args), Args...>;
#include <iostream>
#include <iomanip>
int main() {
Example example(int{3}, float{5.7}, bool{true});
std::cout
<< std::boolalpha << "size "
<< std::tuple_size_v<decltype(example.t)> << " tuple "
<< std::get<0>(example.t) << " " << std::get<1>(example.t)
<< " " << std::get<2>(example.t) << std::endl;
}
Output:
size 3 tuple 3 5.7 true
Suppose I want to implement something like std::tuple myself, just the basics. I want to show a failed attempt first.
#include <utility>
#include <iostream>
template <std::size_t I>
struct tuple_index_leaf {
using Index = std::integral_constant<std::size_t, I>;
std::size_t i = Index::value;
};
template <std::size_t... Is>
struct tuple_index : tuple_index_leaf<Is>...
{};
template <std::size_t I, std::size_t... Is>
constexpr auto get_index(tuple_index<Is...> const &i) {
return static_cast<const tuple_index_leaf<I>*>(&i)->i;
}
template <std::size_t I, typename T>
struct tuple_leaf : tuple_index_leaf<I> {
T elem;
};
template<typename... Ts>
struct tuple : tuple_leaf<sizeof...(Ts), Ts>... {
};
template <std::size_t I, typename... Ts>
auto& get(tuple<Ts...> &t) {
return static_cast<tuple_leaf<I, float>*>(&t)->elem;
}
int main() {
tuple_index<0, 1, 2> ti;
std::cout << get_index<0>(ti) << "\n";
tuple<int, float> t;
get<2>(t) = 3.14;
}
Now, take a look at get function. I hard coded the last type float and I can only call this with index 2, like get<2>. This is because the deficiency in my tuple constructor. If you look there, you will see that I am passing sizeof...(Ts) to tuple_leaf. For example, in this case, all my tuple leaves will be like tuple_leaf<2, int>, tuple_leaf<2, float>. What I wanted was an expansion like tuple_leaf<0, int>, tuple_leaf<1, float>.... The expansion I used, tuple_leaf<sizeof...(Ts), Ts>... does not give me these, I know. I need some sort of index sequence I figured and started implementing something like tuple_index. But that one requires me to pass std::size_t... and I don't know how to do that. So the question is, how can I get an expansion like tuple_leaf<0, int>, tuple_leaf<1, float>...?
It is not hard. Here is one example how to do this (not claiming the only one way, this was something I put together quickly):
#include <utility>
#include <cstddef>
template <std::size_t I, typename T>
struct tuple_leaf {
T elem;
};
template<class SEQ, class... TYPE> struct tuple_impl;
template<size_t... Ix, class... TYPE>
struct tuple_impl<std::index_sequence<Ix...>, TYPE...> : tuple_leaf<Ix, TYPE>... { };
template<typename... Ts>
struct tuple : tuple_impl<std::make_index_sequence<sizeof...(Ts)>, Ts...> { };
// below lines are for testing
tuple<int, double, char*> tup;
// the fact that this compiles tells us char* has index 2
auto& z = static_cast<tuple_leaf<2, char*>&>(tup);
I am using GCC8.2 and I would like to define a hierarchical tuple like this:
std::tuple<std::array<double,N>,
std::array<double,N/2>,
std::array<double,N/4>,
...,
std::array<double,2> > v ;
And then I have an algorithm to fill those arrays with the following specification:
template <int N>
std::array<double,N> get_array()
How can I write a generic algorithm to declare the tuple and fill it at compile time for any N?
Without recursion, you might do something like:
template <std::size_t N>
std::array<double, N> get_array() { return {{}}; }
namespace detail
{
constexpr std::size_t log2(std::size_t n)
{
std::size_t res = 0;
while (n != 0) {
n /= 2;
++res;
}
return res;
}
template <std::size_t N, std::size_t ...Is>
auto make_array_tuple_impl(std::index_sequence<Is...>)
{
return make_tuple(get_array<(N >> Is)>()...);
}
}
template <std::size_t N>
auto make_array_tuple()
{
return detail::make_array_tuple_impl<N>(std::make_index_sequence<detail::log2(N) - 1>());
}
Demo
This feels a bit involved, maybe there's a simpler solution.
// Pack of values
template <auto...>
struct vpack { };
// Concatenating two packs
template <class, class>
struct vpack_cat_;
template <auto... lhs, auto... rhs>
struct vpack_cat_<vpack<lhs...>, vpack<rhs...>> {
using type = vpack<lhs..., rhs...>;
};
template <class Lhs, class Rhs>
using vpack_cat = typename vpack_cat_<Lhs, Rhs>::type;
// Building a decreasing exp scale...
template <int N>
struct exp_scale_ {
using type = vpack_cat<
vpack<N>,
typename exp_scale_<N / 2>::type
>;
};
// ... stopping at 2
template <>
struct exp_scale_<2> {
using type = vpack<2>;
};
template <int N>
using exp_scale = typename exp_scale_<N>::type;
// Building the tuple's type from the scale
template <class ScalePack>
struct exp_tuple_;
template <auto... Scale>
struct exp_tuple_<vpack<Scale...>> {
using type = std::tuple<std::array<double, Scale>...>;
};
template <class Scale>
using exp_tuple = typename exp_tuple_<Scale>::type;
// The known get_array() function
template <int N>
std::array<double,N> get_array() { return {}; }
// Initializing the tuple
template <auto... Scale>
auto get_tuple(vpack<Scale...>) {
return exp_tuple<vpack<Scale...>>{
get_array<Scale>()...
};
}
template <int N>
auto get_tuple() {
return get_tuple(exp_scale<N>{});
}
See it live on Coliru
Are you sure that your sequence in
std::tuple<std::array<double,N>,
std::array<double,N/2>,
std::array<double,N/4>,
...,
std::array<double,2> > v ;
ends with 2?
Supposing that you want to end with 2 or 1, I suppose you can use the following custom type traits to get the wanted index sequence
template <std::size_t N, std::size_t ... Is>
struct divSequence : public divSequence<(N>>1u), Is..., (N>>1u)>
{ };
template <std::size_t ... Is>
struct divSequence<2u, Is...> : public std::index_sequence<Is...>
{ };
template <std::size_t ... Is>
struct divSequence<1u, Is...> : public std::index_sequence<Is...>
{ };
So you only need the following make function (with helper)
template <std::size_t ... Is>
std::tuple<std::array<double, Is>...>
getDivTupleHelper (std::index_sequence<Is...> const &)
{ return { get_array<Is>()... }; }
template <std::size_t N>
auto getDivTuple ()
{ return getDivTupleHelper(divSequence<N>{}); }
The following is a full compiling C++14 example
#include <array>
#include <tuple>
#include <utility>
template <std::size_t N>
std::array<double,N> get_array ()
{ return {{ }}; }
template <std::size_t N, std::size_t ... Is>
struct divSequence : public divSequence<(N>>1u), Is..., (N>>1u)>
{ };
template <std::size_t ... Is>
struct divSequence<2u, Is...> : public std::index_sequence<Is...>
{ };
template <std::size_t ... Is>
struct divSequence<1u, Is...> : public std::index_sequence<Is...>
{ };
template <std::size_t ... Is>
std::tuple<std::array<double, Is>...>
getDivTupleHelper (std::index_sequence<Is...> const &)
{ return { get_array<Is>()... }; }
template <std::size_t N>
auto getDivTuple ()
{ return getDivTupleHelper(divSequence<N>{}); }
int main ()
{
using t0 = decltype( getDivTuple<15u>() );
using t1 = std::tuple<std::array<double, 7u>,
std::array<double, 3u>,
std::array<double, 1u>>;
using t2 = decltype( getDivTuple<16u>() );
using t3 = std::tuple<std::array<double, 8u>,
std::array<double, 4u>,
std::array<double, 2u>>;
static_assert( std::is_same<t0, t1>::value, "!");
static_assert( std::is_same<t2, t3>::value, "!");
}
If you need a C++11 solution... well... instead of std::index_sequence you can use a custom trivial substitute as
template <std::size_t ...>
struct myIndexSequence
{ }
and you need rewrite getDivTuple() to explicit the return type using decltype(); something as
template <std::size_t N>
decltype(getDivTupleHelper(divSequence<N>{})) getDivTuple ()
{ return getDivTupleHelper(divSequence<N>{}); }
I want to be able to do the following:
#include <array>
struct blah { };
template<typename... Args>
constexpr auto foo(Args&&... args)
{
return std::array<blah, sizeof...(Args)>{{ args... }};
}
auto res = foo({}, {});
The following answers aren't satisfying: they just want to check that the parameter pack is of a single type, but I want to convert the values right to it in the arguments (else it does not work).
C++ parameter pack, constrained to have instances of a single type?
Parameter with non-deduced type after parameter pack
Specifying one type for all arguments passed to variadic function or variadic template function w/out using array, vector, structs, etc?
I also can't use initializer_list since I wouldn't be able to count the number of arguments to pass to the array type.
And I especially don't want to type foo(blah{}, blah{});.
What are my possibilities ?
A little bit expanded approach of Jarod42 for lazies (C++17):
#include <utility>
#include <array>
struct blah {};
template <class T, std::size_t I>
using typer = T;
template <class T, std::size_t N, class = std::make_index_sequence<N>>
struct bar_impl;
template <class T, std::size_t N, std::size_t... Is>
struct bar_impl<T, N, std::index_sequence<Is...>> {
static auto foo(typer<T, Is>... ts) {
return std::array<T, N>{{ts...}};
}
};
template <class T = blah, std::size_t N = 10, class = std::make_index_sequence<N>>
struct bar;
template <class T, std::size_t N, std::size_t... Is>
struct bar<T, N, std::index_sequence<Is...>>: bar_impl<T, Is>... {
using bar_impl<T, Is>::foo...;
};
int main() {
bar<>::foo({}, {});
}
[live demo]
Edit:
Some C++14 solution which (as noted by max66) is even simpler than I expected:
#include <utility>
#include <array>
struct blah {};
template <class T, std::size_t I>
using typer = T;
template <class T = blah, std::size_t N = 10, class = std::make_index_sequence<N>>
struct bar;
template <class T, std::size_t N, std::size_t... Is>
struct bar<T, N, std::index_sequence<Is...>>: bar<T, N - 1> {
using bar<T, N - 1>::foo;
static auto foo(typer<T, Is>... ts) {
return std::array<T, N>{{ts...}};
}
};
template <class T>
struct bar<T, 0, std::index_sequence<>> {
static auto foo() {
return std::array<T, 0>{{}};
}
};
int main() {
bar<>::foo({}, {});
}
[live demo]
One more edit:
This one (as suggested by Jarod42) provides exactly the same syntax for invocation as in OP's question:
#include <utility>
#include <array>
struct blah {};
template <class T, std::size_t I>
using typer = T;
template <class T = blah, std::size_t N = 10, class = std::make_index_sequence<N>>
struct bar;
template <class T, std::size_t N, std::size_t... Is>
struct bar<T, N, std::index_sequence<Is...>>: bar<T, N - 1> {
using bar<T, N - 1>::operator();
auto operator()(typer<T, Is>... ts) {
return std::array<T, N>{{ts...}};
}
};
template <class T>
struct bar<T, 0, std::index_sequence<>> {
auto operator()() {
return std::array<T, 0>{{}};
}
};
bar<> foo;
int main() {
foo({}, {});
}
[live demo]
Alright, if you can afford to change the syntax a little bit, this is the best I managed to find:
#include <array>
// to_array implementation taken from
// http://en.cppreference.com/w/cpp/experimental/to_array
namespace detail {
template <class T, std::size_t N, std::size_t... I>
constexpr std::array<std::remove_cv_t<T>, N>
to_array_impl(T (&a)[N], std::index_sequence<I...>)
{
return { {a[I]...} };
}
}
template <class T, std::size_t N>
constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N])
{
return detail::to_array_impl(a, std::make_index_sequence<N>{});
}
// End of to_array implementation
struct blah { };
template<std::size_t N>
constexpr auto foo(const blah(&arr)[N])
{
return to_array(arr);
}
int main()
{
auto res = foo({{}, {}});
return 0;
}
As you can see, foo({}, {}) became foo({{}, {}}).
Here is a working example: https://ideone.com/slbKi3
The issue with the way you want it (foo({}, {})) is that the compiler has no way to know what it is supposed to convert {} to.
I tried to find a way to let it know but it didn't listen at all.
If you accept, as proposed by Telokis, to add a bracket level calling foo()
auto res = foo( { {}, {} } );
you can use the C-style array trick proposed by Telokis and a simple cycle to initialize the returned value
template <std::size_t N>
constexpr std::array<blah, N> foo (const blah(&arr)[N])
{
std::array<blah, N> ret;
for ( auto i = 0U ; i < N ; ++i )
ret[i] = arr[i];
return ret;
}
Unfortunately the operator[] for std::array is constexpr only starting from C++17, so the preceding foo is effectively constexpr only starting from C++17.
So you can call
auto res = foo( { {}, {} } );
also in C++11 and C++14, but
constexpr auto res = foo( { {}, {} } );
only starting from C++17.
One (limited) ways to keep your syntax is to have several overloads:
constexpr auto foo(const blah& a1)
{
return std::array<blah, 1>{{ a1 }};
}
constexpr auto foo(const blah& a1, const blah& a2)
{
return std::array<blah, 2>{{ a1, a2 }};
}
// ...
// Up to N
constexpr auto foo(const blah& a1, const blah& a2, .., const blah& aN)
{
return std::array<blah, N>{{ a1, a2, .., aN }};
}
W.F. in his answer shows a way to generate it thanks to variadic at class scope.
In C++17 you can use a combination of static_assert and std::conjunction like this:
#include <array>
#include <type_traits>
struct blah {};
template <typename Arg, typename... Args>
constexpr auto foo_helper(Arg&& first, Args&&... rest) {
static_assert(std::conjunction_v<std::is_same<Arg, Args>...>);
return std::array<blah, 1 + sizeof...(Args)>{first, rest...};
}
template <typename... Args>
constexpr auto foo(Args&&... args) {
return foo_helper(std::forward<Args>(args)...);
}
auto res = foo(blah{}, blah{})
I tried the following recursion to write out the elements of a tuple, but I have trouble in the line that computes the tuple size at compile time (commented out below):
#include <tuple>
#include <string>
#include <iostream>
template<typename Tuple, std::size_t element = 0>
struct write_tuple
{
static void execute(Tuple const & t)
{
std::cout << std::get<element>(t) << std::endl;
write_tuple<Tuple, element + 1>::execute(t);
}
};
template<typename Tuple>
struct write_tuple<Tuple, 4> // This works fine
//struct write_tuple<Tuple, std::tuple_size<typename Tuple>::value > // std::tuple_size should give me the size of the tuple at compile-time
{
static void execute(Tuple const & t) {};
};
template<typename Tuple>
void write(Tuple const & t)
{
write_tuple<Tuple>::execute(t);
}
using namespace std;
int main(int argc, const char *argv[])
{
tuple<string, int, double, string> myTuple = std::make_tuple("test", 0, 2.1, "finished");
write(myTuple);
return 0;
}
The line:
struct write_tuple<Tuple, 4>
works perfectly fine to terminate the recursive call for a tuple of size 4, but when I use the std::tuple_size<typename Tuple>::value to get the tuple size at compile time, I get the following error:
main.cpp:17:57: error: template argument 1 is invalid
struct write_tuple<Tuple, std::tuple_size<typename Tuple>::value > // std::tuple_size should give me the size of the tuple at compile-time
^
main.cpp:17:66: error: template argument 2 is invalid
struct write_tuple<Tuple, std::tuple_size<typename Tuple>::value > // std::tuple_size should give me the size of the tuple at compile-time
I am using gcc 4.8.2.
Edit:
removing the typename from std::tuple_size<typename Tuple>::value results with the following error:
g++ -std=c++11 main.cpp -o main 2>&1 | tee log
main.cpp:17:8: error: template argument ‘std::tuple_size<_Tp>::value’ involves template parameter(s)
struct write_tuple<Tuple, std::tuple_size<Tuple>::value> // std::tuple_size should give me the size of the tuple at compile-time
For your code, you may reverse the recursion, like:
template<typename Tuple, std::size_t remaining>
struct write_tuple
{
static void execute(Tuple const & t)
{
std::cout << std::get<std::tuple_size<Tuple>::value - remaining>(t) << std::endl;
write_tuple<Tuple, remaining - 1>::execute(t);
}
};
template<typename Tuple>
struct write_tuple<Tuple, 0>
{
static void execute(Tuple const & t) {};
};
template<typename Tuple>
void write(Tuple const & t)
{
write_tuple<Tuple, std::tuple_size<Tuple>::value>::execute(t);
}
An alternative approach:
#include <tuple>
#include <iostream>
#include <cstdint>
#if 1 // Not in C++11
template <std::size_t ...> struct index_sequence {};
template <std::size_t N, std::size_t ...Is>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {};
template <std::size_t ... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> {};
#endif // make_index_sequence
template<typename Tuple>
struct write_tuple
{
static void execute(Tuple const & t)
{
execute(t, make_index_sequence<std::tuple_size<Tuple>::value>());
}
private:
template<std::size_t ... Is>
static void execute(Tuple const & t, index_sequence<Is...>)
{
const int dummy[] = {0, (write_element(std::get<Is>(t)), 0)...};
static_cast<void>(dummy); // silent warning for unused variable.
}
template <typename T>
static void write_element(T const &elem)
{
std::cout << elem << std::endl;
}
};
It is a quirk of the language: non-type parameter values cannot be dependent on type parameter values in a specialization. Probably the standardization comittee had a reason. It may have been a good one.
There are a few ways to solve your problem, the easiest beimg to recurse down to 0 and print on the way back up the recursion. One that involves the least change to your code would be to add a , typename=void> parameter to your class template, and in the specialization add the size in, then , size, typename std::enable_if<size==std::tuple_size<Tuple>::value>::type> added to the end of the specialization. This makes the test in a type parameter, specialized on void (which is always there), but only valid when SFINAE succeeds and the size matches the tuple size.