I want to convert an "array" of bool to a integer sequence.
So I need to compute an std::array at compile time.
Here is my code
#include <array>
template<typename InputIt, typename T >
inline constexpr typename std::iterator_traits<InputIt>::difference_type
count( InputIt first, InputIt last, const T &value ) {
typename std::iterator_traits<InputIt>::difference_type ret = 0;
for (; first != last; ++first) {
if (*first == value) {
ret++;
}
}
return ret;
}
template<bool ..._values>
struct keep_value {
static constexpr std::size_t numberOfValues = sizeof...(_values);
static constexpr bool values[] = {_values...};
static constexpr std::size_t numberToKeep = count(values, values + numberOfValues, true);
static constexpr std::array<std::size_t, numberToKeep> computeIndices() {
std::array<std::size_t, numberToKeep> array{};
auto it = array.begin();
for(std::size_t i{0}; i < numberOfValues; ++i)
if(values[i] == true)
*it++ = i;
return array;
}
static constexpr std::array<std::size_t, numberToKeep> indices = computeIndices();
template<typename Indices = std::make_index_sequence<numberToKeep>>
struct as_index_sequence{};
template<std::size_t ...Is>
struct as_index_sequence<std::index_sequence<Is...>> : std::index_sequence<indices[Is]...>{};
};
int main() {
keep_value<false, true, true>::template as_index_sequence<>{}; // Should return the sequence 1 2
}
I get an error for the line that call the computeIndices function. Is this code c++14 correct? Is it possible to do otherwise?
I am using MSVC and I get this error :
expression did not evaluate to a constant
This code looks correct and works when compiled as C++17.
It uses std::array::begin, which only has been made constexpr in C++17.
A better compilation error can be achieved when using clang, which states:
<source>:23:25: note: non-constexpr function 'begin' cannot be used in a constant expression
auto it = array.begin();
Is it possible to do otherwise?
About correctness answered JVApen (+1).
A possible alternative is avoid std::array at all and construct the index sequence in a recursive way using template specialization
The following is a full compilable example
#include <utility>
#include <type_traits>
template <typename, std::size_t, bool...>
struct bar;
// true case
template <std::size_t ... Is, std::size_t I, bool ... Bs>
struct bar<std::index_sequence<Is...>, I, true, Bs...>
: public bar<std::index_sequence<Is..., I>, I+1U, Bs...>
{ };
// false case
template <std::size_t ... Is, std::size_t I, bool ... Bs>
struct bar<std::index_sequence<Is...>, I, false, Bs...>
: public bar<std::index_sequence<Is...>, I+1U, Bs...>
{ };
// end case
template <typename T, std::size_t I>
struct bar<T, I>
{ using type = T; };
template <bool ... Bs>
struct foo : public bar<std::index_sequence<>, 0U, Bs...>
{ };
int main()
{
static_assert( std::is_same<typename foo<false, true, true>::type,
std::index_sequence<1U, 2U>>{}, "!" );
}
If you don't like the recursive solutions, I propose (just for fun) another solution based of std::tuple_cat
#include <tuple>
#include <utility>
#include <type_traits>
template <std::size_t, bool>
struct baz
{ using type = std::tuple<>; };
template <std::size_t I>
struct baz<I, true>
{ using type = std::tuple<std::integral_constant<std::size_t, I>>; };
template <std::size_t I, bool B>
using baz_t = typename baz<I, B>::type;
template <typename, bool...>
struct bar;
template <std::size_t ... Is, bool ... Bs>
struct bar<std::index_sequence<Is...>, Bs...>
{
template <std::size_t ... Js>
constexpr static std::index_sequence<Js...>
func (std::tuple<std::integral_constant<std::size_t, Js>...> const &);
using type = decltype(func(std::tuple_cat(baz_t<Is, Bs>{}...)));
};
template <bool ... Bs>
struct foo : public bar<std::make_index_sequence<sizeof...(Bs)>, Bs...>
{ };
int main()
{
static_assert( std::is_same<typename foo<false, true, true>::type,
std::index_sequence<1U, 2U>>{}, "!" );
}
Related
Upon calling std::contional<bool, T1, T2> both the types T1 and T2 get instantiated as soon as its called, irrespective of the value of the bool, does there exist an implementation (or some pointers to implement it) where only the relevant type is instantiated.
Following code for example is impossible to write using std::conditional. We can't even use SFINAE over structs (get in the example).
struct Term {};
template <int N, typename T, typename ...Ts>
struct PairList
{
static constexpr int i = N;
using type = T;
using tail = PairList<N + 1, Ts...>;
};
template <int N, typename T>
struct PairList<N, T>
{
static constexpr int i = N;
using type = T;
using tail = Term;
};
template <int N, typename pairList>
struct get
{
static constexpr auto good = (N == pairList::i);
using T = typename std::conditional<
good,
typename pairList::type,
typename get<N, typename pairList::tail>::T>::type; // The second parameter might not be defined
};
You can use if constexpr to lazily instantiate templates while keeping familiar code structure:
// C++20: std::type_identity
template<typename T>
struct type_t {
using type = T;
};
template <int N, typename pairList>
struct get
{
static auto choose_type() {
if constexpr (N == pairList::i) {
return type_t<typename pairList::type>{};
} else {
return type_t<typename get<N, typename pairList::tail>::T>{};
}
}
using T = typename decltype(choose_type())::type;
};
I usually use template specialization to avoid this sort of problem
// bool is false -> recursion
template <int N, typename pairList, bool = N == pairList::i>
struct get : public get<N, typename pairList::tail>
{ };
// bool is true -> stop recursion and type is selected
template <int N, typename pairList>
struct get<N, pairList, true>
{ using T = typename pairList::type; };
The following is a full compiling example
#include <type_traits>
struct Term {};
template <int N, typename T, typename ...Ts>
struct PairList
{
static constexpr int i = N;
using type = T;
using tail = PairList<N + 1, Ts...>;
};
template <int N, typename T>
struct PairList<N, T>
{
static constexpr int i = N;
using type = T;
using tail = Term;
};
// bool is false -> recursion
template <int N, typename pairList, bool = N == pairList::i>
struct get : public get<N, typename pairList::tail>
{ };
// bool is true -> stop recursion and type is selected
template <int N, typename pairList>
struct get<N, pairList, true>
{ using T = typename pairList::type; };
int main ()
{
using T0 = PairList<0u, int, long, long long>;
using T1 = get<1u, T0>::T;
static_assert( std::is_same_v<T1, long>, "!" );
}
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>{}); }
Say I have a class:
template<typename... Types>
class Example
{
public:
using types = std::tuple<Types...>;
template<size_t N> using dim_type = std::tuple_element_t<N, types>;
};
And I want to implement a member function that depends on the tuple element as follows, with the goal of having access to the number of arguments during compilation:
template<size_t N>
inline constexpr void do_stuff(const dim_type<N>& elems...)
{
constexpr size_t size_stuff = sizeof...(elems); // <-- this is the end goal
};
Problem is that this implementation of do_stuff() won't do. And ultimately I would like the function to work like this:
Example<long,std::string> ex;
ex.do_stuff<0>(3);
ex.do_stuff<0>(5,6);
ex.do_stuff<1>("a","b","c","d");
ex.do_stuff<0>("a","b","c","d"); // <-- this line should not compile
ex.do_stuff<1>(1,"b"); // <-- nor should this one
And inside do_stuff I should know at compile-time how many arguments are passed.
One possible answer, but I'm having an issue with it:
I figured that a proper implementation of this would require the use of variadic templates, however, I am having a problem getting the std::enable_if part to work:
template<size_t N, typename... Args,
std::enable_if_t<static_and<std::is_convertible_v<Args, dim_type<N>>...>::value>* = nullptr>
inline constexpr void do_stuff(const Args&... elems)
{
constexpr size_t size_stuff = sizeof...(elems); // <-- this is the end goal
};
where static_and is:
template<bool Head, bool... Tail>
struct static_and {
static constexpr bool value = Head && static_and<Tail...>::value;
};
template<bool Bool> struct static_and<Bool> {
static constexpr bool value = Bool;
};
It seems to me that the right way (a possible right way) is the one based on static_and: a variadic type list Args of argument that is SFINAE checked to be convertible to the right type.
I propose the following version of static_and
template <bool ...>
struct static_and : public std::false_type
{ };
template <>
struct static_and<> : public std::true_type
{ };
template <bool ... Bs>
struct static_and<true, Bs...> : public static_and<Bs...>
{ };
and do_stuff() become
template <std::size_t I, typename ... Ts>
inline constexpr auto do_stuff (Ts const & ... elems)
-> std::enable_if_t<
static_and<std::is_convertible<Ts, type_n<I>>::value...>::value>
{
// now the number of elems is sizeof...(elems)
// or sizeof...(Ts)
std::cout << sizeof...(Ts) << std::endl;
}
The following is a full compiling example (with compilation errors when appropriate)
#include <tuple>
#include <iostream>
#include <type_traits>
template <bool ...>
struct static_and : public std::false_type
{ };
template <>
struct static_and<> : public std::true_type
{ };
template <bool ... Bs>
struct static_and<true, Bs...> : public static_and<Bs...>
{ };
template <typename ... Types>
struct foo
{
using types = std::tuple<Types...>;
static constexpr std::size_t num_types { sizeof...(Types) };
template <std::size_t I>
using type_n = std::tuple_element_t<I, types>;
template <std::size_t I, typename ... Ts>
inline constexpr auto do_stuff (Ts const & ... elems)
-> std::enable_if_t<
static_and<std::is_convertible<Ts, type_n<I>>::value...>::value>
{
// now the number of elems is sizeof...(elems)
// or sizeof...(Ts)
std::cout << sizeof...(Ts) << std::endl;
}
};
int main ()
{
foo<long, std::string> ex;
ex.do_stuff<0>(3); // compile; print 1
ex.do_stuff<0>(5, 6); // compile; print 2
ex.do_stuff<1>("a", "b", "c", "d"); // compile; print 4
// ex.do_stuff<0>("a", "b", "c", "d"); // compilation error
// ex.do_stuff<1>(1, "b"); // compilation error
}
If you can use C++17, instead of static_and you can simply use template folding
template <std::size_t I, typename ... Ts>
inline constexpr auto do_stuff (Ts const & ... elems)
-> std::enable_if_t<(... && std::is_convertible<Ts, type_n<I>>::value)>
{
// now the number of elems is sizeof...(elems)
// or sizeof...(Ts)
std::cout << sizeof...(Ts) << std::endl;
}
If, in C++14, you prefer a constexpr static_and() function instead of a struct, you can write it as follows
template <bool ... Bs>
constexpr bool static_and ()
{
using unused = bool[];
bool ret { true };
(void) unused { true, ret &= Bs... };
return ret;
}
So do_stuff() become
template <std::size_t I, typename ... Ts>
inline constexpr auto do_stuff (Ts const & ... elems)
-> std::enable_if_t<
static_and<std::is_convertible<Ts, type_n<I>>::value...>()>
{
// now the number of elems is sizeof...(elems)
// or sizeof...(Ts)
std::cout << sizeof...(Ts) << std::endl;
}
I'm trying to determine how to take in a boolean template parameter pack
template<bool...B> struct BoolPack{};
and construct from it an integer parameter pack where the values indicate the enumeration of the true elements in BoolPack. The enumeration could be contained like this:
template<size_t...I> struct IntegerSequence{};
The function should take in a BoolPack<B...> and return an IntegerSequence<I...>;
template<bool...B>
constexpr auto enumerate( const BoolPack<B...>& b ) {
// Combination of cumulative sum and mask?
return IntegerSequence<???>();
}
For example, if the input would be
BoolPack<true,false,true,false,true> b;
The function should return
IntegerSequence<1,0,2,0,3> e();
My best guess on how to implement this would be to compute a partial sum
where the the kth template parameter is
K = K-1 + static_cast<size_t>(get<K>(make_tuple<B...>));
I'm not sure if this is the way to do it. Is there not a more direct approach that doesn't require making tuples for example? Applying the recursion should result in
IntegerSequence<1,1,2,2,3> s();
And then to multiply this componentwise with the elements of the original BoolPack. Is this the way to go, or can I do this without tuples?
Thanks!
The way you think to implement
template<bool...B>
constexpr auto enumerate( const BoolPack<B...>& b ) {
// Combination of cumulative sum and mask?
return IntegerSequence<???>();
}
can't work because C++ is a strong typed language, so the returned type can't depend from sums obtained from code that can be executed compile time.
I know that the values are known at compile time, so the values can be computed compile-time; but the best way that I see pass through a specific type traits.
By example (sorry: renamed IntegerSequence as IndexSequence to be more near to the C++14 std::index_sequence)
template <std::size_t, typename, std::size_t ...>
struct BoolIndexer
{ };
template <std::size_t N, bool ... Bs, std::size_t ... Is>
struct BoolIndexer<N, BoolPack<true, Bs...>, Is...>
: public BoolIndexer<N+1U, BoolPack<Bs...>, Is..., N>
{ };
template <std::size_t N, bool ... Bs, std::size_t ... Is>
struct BoolIndexer<N, BoolPack<false, Bs...>, Is...>
: public BoolIndexer<N, BoolPack<Bs...>, Is..., 0U>
{ };
template <std::size_t N, std::size_t ... Is>
struct BoolIndexer<N, BoolPack<>, Is...>
{ using type = IndexSequence<Is...>; };
The following is a full compiling example
#include <iostream>
#include <type_traits>
template <bool ... B>
struct BoolPack
{ };
template<size_t...I>
struct IndexSequence
{ };
template <std::size_t, typename, std::size_t ...>
struct BoolIndexer
{ };
template <std::size_t N, bool ... Bs, std::size_t ... Is>
struct BoolIndexer<N, BoolPack<true, Bs...>, Is...>
: public BoolIndexer<N+1U, BoolPack<Bs...>, Is..., N>
{ };
template <std::size_t N, bool ... Bs, std::size_t ... Is>
struct BoolIndexer<N, BoolPack<false, Bs...>, Is...>
: public BoolIndexer<N, BoolPack<Bs...>, Is..., 0U>
{ };
template <std::size_t N, std::size_t ... Is>
struct BoolIndexer<N, BoolPack<>, Is...>
{ using type = IndexSequence<Is...>; };
int main ()
{
using type1 = BoolPack<true,false,true,false,true>;
using type2 = typename BoolIndexer<1U, type1>::type;
static_assert( std::is_same<type2, IndexSequence<1,0,2,0,3>>{}, "!" );
}
If you really need a function to get the conversion, using the BoolIndexer type traits you can write it simply as follows
template <bool ... Bs>
constexpr auto getIndexSequence (BoolPack<Bs...> const &)
-> typename BoolIndexer<1U, BoolPack<Bs...>>::type
{ return {}; }
and call it in this way
auto is = getIndexSequence(BoolPack<true,false,true,false,true>{});
Another possible solution pass through the creation of a constexpr function
template <bool ... Bs>
constexpr std::size_t getNumTrue (BoolPack<Bs...> const &, std::size_t top)
{
using unused = int[];
std::size_t cnt = -1;
std::size_t ret { 0 };
(void)unused { 0, (++cnt <= top ? ret += Bs : ret, 0)... };
return ret;
}
that can be called to set the template values for IndexSequence; unfortunately the following example use std::make_index_sequence and std::index_sequence that are C++14 (starting from) features
template <bool ... Bs, std::size_t ... Is>
constexpr auto gisH (BoolPack<Bs...> const &,
std::index_sequence<Is...> const &)
-> IndexSequence<(Bs ? getNumTrue(BoolPack<Bs...>{}, Is) : 0U)...>
{ return {}; }
template <bool ... Bs>
constexpr auto getIndexSequence (BoolPack<Bs...> const & bp)
{ return gisH(bp, std::make_index_sequence<sizeof...(Bs)>{}); }
The following is a full compiling example
#include <utility>
#include <iostream>
#include <type_traits>
template <bool ... B>
struct BoolPack
{ };
template <std::size_t...I>
struct IndexSequence
{ };
template <bool ... Bs>
constexpr std::size_t getNumTrue (BoolPack<Bs...> const &, std::size_t top)
{
using unused = int[];
std::size_t cnt = -1;
std::size_t ret { 0 };
(void)unused { 0, (++cnt <= top ? ret += Bs : ret, 0)... };
return ret;
}
template <bool ... Bs, std::size_t ... Is>
constexpr auto gisH (BoolPack<Bs...> const &,
std::index_sequence<Is...> const &)
-> IndexSequence<(Bs ? getNumTrue(BoolPack<Bs...>{}, Is) : 0U)...>
{ return {}; }
template <bool ... Bs>
constexpr auto getIndexSequence (BoolPack<Bs...> const & bp)
{ return gisH(bp, std::make_index_sequence<sizeof...(Bs)>{}); }
int main()
{
using typeBP = BoolPack<true,false,true,false,true>;
auto is = getIndexSequence(typeBP{});
static_assert( std::is_same<decltype(is),
IndexSequence<1,0,2,0,3>>{}, "!" );
}
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{})