Here is a snippet from my code:
template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns>
class HexMatrix
{
private:
std::array<Type, NumberOfRows*NumberOfColumns> values;
// ...
public:
template <typename... OtherType>
HexMatrix(OtherType...);
// ...
};
template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns>
template <typename... OtherType>
HexMatrix<Type, NumberOfRows, NumberOfColumns>::HexMatrix(OtherType... args) : values({ args... })
{
static_assert(NumberOfRows != 0u);
static_assert(NumberOfColumns != 0u);
}
Until recently, I was able to use this code as is, and initialise the std::array with an std::vector using the constructor above. Then my PC got formatted. Now this doesn't compile anymore, even though I was using and am still using -std=c++2a. Here is the compilation report:
In file included from foo.cpp:3:
LinearAlgebra.hpp: In instantiation of ‘HexMatrix< <template-parameter-1-1>, <anonymous>, <anonymous> >::HexMatrix(OtherType ...) [with OtherType = {std::vector<long double, std::allocator<long double> >}; Type = long double; unsigned int NumberOfRows = 6; unsigned int NumberOfColumns = 6]’:
LinearAlgebra.hpp:230:9: required from ‘static HexMatrix<Type, NumberOfRows, NumberOfColumns> HexMatrix< <template-parameter-1-1>, <anonymous>, <anonymous> >::Make(OtherType ...) [with OtherType = {std::vector<long double, std::allocator<long double> >}; Type = long double; unsigned int NumberOfRows = 6; unsigned int NumberOfColumns = 6]’
foo.cpp:93:80: required from here
LinearAlgebra.hpp:299:98: error: no matching function for call to ‘std::array<long double, 36>::array(<brace-enclosed initializer list>)’
299 | HexMatrix<Type, NumberOfRows, NumberOfColumns>::HexMatrix(OtherType... args) : values({ args... })
| ^
In file included from /usr/include/c++/9/tuple:39,
from /usr/include/c++/9/bits/hashtable_policy.h:34,
from /usr/include/c++/9/bits/hashtable.h:35,
from /usr/include/c++/9/unordered_map:46,
from Using.hpp:5,
from LinearAlgebra.hpp:4,
from foo.cpp:3:
/usr/include/c++/9/array:94:12: note: candidate: ‘std::array<long double, 36>::array()’
94 | struct array
| ^~~~~
/usr/include/c++/9/array:94:12: note: candidate expects 0 arguments, 1 provided
/usr/include/c++/9/array:94:12: note: candidate: ‘constexpr std::array<long double, 36>::array(const std::array<long double, 36>&)’
/usr/include/c++/9/array:94:12: note: no known conversion for argument 1 from ‘<brace-enclosed initializer list>’ to ‘const std::array<long double, 36>&’
/usr/include/c++/9/array:94:12: note: candidate: ‘constexpr std::array<long double, 36>::array(std::array<long double, 36>&&)’
/usr/include/c++/9/array:94:12: note: no known conversion for argument 1 from ‘<brace-enclosed initializer list>’ to ‘std::array<long double, 36>&&’
make: *** [Makefile:16: foo.o] Error 1
What's wrong? I tried to remove the parenthesis, and/or add another pair of brackets around args... but that didn't change anything. Please help?
Reproductible example:
int main(void)
{
const std::vector<long double> foo = { 1.L, 2.L, -6.L, 0.L };
HexMatrix<long double, 2u, 2u> givenMatrix(foo);
return 0;
}
If you want to pass a vector in the constructor, add a new constructor to HexMatrix:
#include <vector>
#include <array>
#include <algorithm>
template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns>
class HexMatrix
{
private:
std::array<Type, NumberOfRows*NumberOfColumns> values;
// ...
public:
template <typename... OtherType>
HexMatrix(OtherType...);
HexMatrix(const std::vector<Type>&);
// ...
};
template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns>
template <typename... OtherType>
HexMatrix<Type, NumberOfRows, NumberOfColumns>::HexMatrix(OtherType... args) : values({ args... })
{
static_assert(NumberOfRows != 0u);
static_assert(NumberOfColumns != 0u);
}
template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns>
HexMatrix<Type, NumberOfRows, NumberOfColumns>::HexMatrix(const std::vector<Type>& vec)
{
static_assert(NumberOfRows != 0u);
static_assert(NumberOfColumns != 0u);
std::copy_n(vec.begin(), NumberOfRows*NumberOfColumns, values.begin());
}
int main(void)
{
const std::vector<long double> foo = { 1.L, 2.L, -6.L, 0.L };
HexMatrix<long double, 2u, 2u> givenMatrix(foo);
return 0;
}
I would like a function bool dominates(const std::tuple<T...>& t1, const std::tuple<T...>& t2) which returns true iff tuple t1 dominates tuple t2, i.e. for all i, t1[i] <= t2[i], in contrast with the default <= operator which uses a lexicographic comparison.
I've tried to adapt the answer from this question, but without success. It fails at compilation.
template<typename H>
bool& dominates_impl(bool& b, H&& h1, H&& h2)
{
b &= std::forward<H>(h1) <= std::forward<H>(h2);
return b;
}
template<typename H, typename... T>
bool& dominates_impl(bool& b, H&& h1, H&& h2, T&&... t1, T&&... t2)
{
b &= (std::forward<H>(h1) <= std::forward<H>(h2));
return dominates_impl(b, std::forward<T>(t1)..., std::forward<T>(t2)...);
}
template<typename... T, std::size_t... I>
bool dominates(
const std::tuple<T...>& t1,
const std::tuple<T...>& t2,
integer_sequence<std::size_t, I...>)
{
bool b = true;
int ctx[] = { (dominates_impl(b, std::get<I>(t1)..., std::get<I>(t2)...), 0), 0};
(void)ctx;
return b;
}
template <typename ... T>
bool dominates(
const std::tuple<T...>& t1,
const std::tuple<T...>& t2)
{
return dominates(t1, t2, gen_indices<sizeof...(T)>{});
}
Compilation errors:
./common.hpp: In instantiation of 'bool dominates(const std::tuple<_Tps ...>&, const std::tuple<_Tps ...>&, integer_sequence<long unsigned int, I ...>) [with T = {long int, long int, long int, long int, long int}; long unsigned int ...I = {0, 1, 2, 3, 4}]':
./common.hpp:107:21: required from 'bool dominates(const std::tuple<_Tps ...>&, const std::tuple<_Tps ...>&) [with T = {long int, long int, long int, long int, long int}]'
examples.cpp:1624:65: required from here
./common.hpp:97:34: error: no matching function for call to 'dominates_impl(bool&, std::__tuple_element_t<0, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<1, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<2, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<3, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<4, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<0, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<1, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<2, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<3, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<4, std::tuple<long int, long int, long int, long int, long int> >&)'
97 | int ctx[] = { (dominates_impl(b, std::get<I>(t1)..., std::get<I>(t2)...), 0), 0};
| ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./common.hpp:77:7: note: candidate: 'template<class H> bool& dominates_impl(bool&, H&&, H&&)'
77 | bool& dominates_impl(bool& b, H&& h1, H&& h2)
| ^~~~~~~~~~~~~~
./common.hpp:77:7: note: template argument deduction/substitution failed:
./common.hpp:97:34: note: candidate expects 3 arguments, 11 provided
97 | int ctx[] = { (dominates_impl(b, std::get<I>(t1)..., std::get<I>(t2)...), 0), 0};
| ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./common.hpp:84:7: note: candidate: 'bool& dominates_impl(bool&, H&&, H&&, T&& ..., T&& ...) [with H = const long int&; T = {const long int&, const long int&, const long int&, const long int&, const long int&, const long int&, const long int&, const long int&}]'
84 | bool& dominates_impl(bool& b, H&& h1, H&& h2, T&&... t1, T&&... t2)
| ^~~~~~~~~~~~~~
./common.hpp:84:7: note: candidate expects 19 arguments, 11 provided
The problem in you code is in dominates_impl()
template<typename H, typename... T>
bool& dominates_impl(bool& b, H&& h1, H&& h2, T&&... t1, T&&... t2)
you can't have two variadic argument list of argument in a function; you can have only one in last position.
But you don't need dominates_impl() at all: you can emulate C++17 template folding writing dominates() (the three argument version) as follows
template<typename... T, std::size_t... I>
bool dominates(
const std::tuple<T...>& t1,
const std::tuple<T...>& t2,
integer_sequence<std::size_t, I...>)
{
using unused = bool[];
bool b { true };
(void)unused { b, (b = b && std::get<I>(t1) <= std::get<I>(t2))... };
return b;
}
Remember to recover integer_sequence and gen_indices() from the original question.
I was able to make it work in C++11, but only by manually reinventing C++14's std::integer_sequence, and losing constexpr-ability:
#include <tuple>
#include <type_traits>
#include <assert.h>
template<typename T, T ...i> struct integer_sequence {};
template<typename T, T v=0>
struct counter {
static constexpr T n=v;
typedef counter<T, v-1> prev;
};
template<typename T, typename V, T ...i> struct integer_sequence_impl;
template<typename T, T ...i>
struct integer_sequence_impl<T, counter<T>, i...> {
typedef struct integer_sequence<T, 0, i...> t;
};
template<typename T, typename V, T ...i> struct integer_sequence_impl
: integer_sequence_impl<T, typename V::prev, V::n, i...> {};
template<typename T, T n>
using create_integer_sequence=
typename integer_sequence_impl<T, counter<T, n-1>>::t;
template<class T, T N>
using make_integer_sequence=create_integer_sequence<T, N>;
template<typename T1,
typename T2,
std::size_t ...i>
bool dominates_impl(const T1 &t1,
const T2 &t2,
const integer_sequence<std::size_t, i...> &)
{
bool compare[]={
(std::get<i>(t1) <= std::get<i>(t2))...
};
for (auto f:compare)
if (!f)
return false;
return true;
}
template<typename ...T1,
typename ...T2,
typename=typename std::enable_if<sizeof...(T1) == sizeof...(T2)>::type>
bool dominates(const std::tuple<T1...> &t1,
const std::tuple<T2...> &t2)
{
return dominates_impl(t1, t2,
make_integer_sequence<std::size_t, sizeof...(T1)>
{});
}
int main()
{
assert(!dominates(std::tuple<int, int>{4, 2},
std::tuple<int, int>{3, 1}));
assert(dominates(std::tuple<int, int>{2, 2},
std::tuple<int, int>{3, 2}));
return 0;
}
A good chunk of the above is a half-baked std::integer_sequence. With that, and C++17's fold expressions this becomes a no-brainer:
#include <tuple>
#include <type_traits>
template<typename T1,
typename T2,
std::size_t ...i>
constexpr bool dominates_impl(const T1 &t1,
const T2 &t2,
const std::integer_sequence<std::size_t, i...> &)
{
return ( (std::get<i>(t1) <= std::get<i>(t2)) && ...);
}
template<typename ...T1,
typename ...T2,
typename=std::enable_if_t<sizeof...(T1) == sizeof...(T2)>>
constexpr bool dominates(const std::tuple<T1...> &t1,
const std::tuple<T2...> &t2)
{
return dominates_impl(t1, t2,
std::make_index_sequence<sizeof...(T1)>{});
}
static_assert(!dominates(std::tuple{4, 2},
std::tuple{3, 1}));
static_assert(dominates(std::tuple{2, 2},
std::tuple{3, 2}));
I have the following types:
struct A { };
struct B { };
struct C { };
template <typename Class, uint16_t i>
struct def {
using message_type = Class;
static constexpr uint16_t tag = i;
};
and this tuple:
constexpr auto types = std::make_tuple(def<A, 1>(), def<B, 2>(), def<C, 3>());
Types A, B and C should be mapped to corresponding values (A -> 1 etc.). I want to create something (function, struct) that given object of one of these types will return proper value. I tried doing the following:
template <typename T>
struct gettag {
static decltype(T::tag) value(typename T::message_type const&) { return T::tag; }
};
template <typename... Args>
struct tagdb : public gettag<Args>... {
tagdb(std::tuple<Args...> const& t) { }
};
int main() {
tagdb t(types);
A a;
std::cout << t.value(a) << '\n';
}
This does not work, g++ claims that request for member value is ambiguous:
x.cc: In function ‘int main()’:
x.cc:29:17: error: request for member ‘value’ is ambiguous
29 | std::cout << t.value(a) << '\n';
| ^~~~~
x.cc:16:26: note: candidates are: ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<C, 3>; decltype (T::tag) = const short unsigned int; typename T::message_type = C]’
16 | static decltype(T::tag) value(typename T::message_type const&) { return T::tag; }
| ^~~~~
x.cc:16:26: note: ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<B, 2>; decltype (T::tag) = const short unsigned int; typename T::message_type = B]’
x.cc:16:26: note: ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<A, 1>; decltype (T::tag) = const short unsigned int; typename T::message_type = A]’
I am a little surprised, especially since it clearly shows that each method is parameterized using different types.
Is there a way to make this solution work or should I completely change my approach? Note that what I want to avoid most is writing overloads for each type.
I suggest a solution without a std::tuple and gettag:
struct A { };
struct B { };
struct C { };
template <typename Class, std::uint16_t i>
struct def {
static constexpr std::uint16_t value(Class) {
return i;
}
};
template <typename... Tags>
struct tagdb : public Tags... {
using Tags::value...;
};
template<class... Tags>
constexpr auto make_tagdb(Tags...) {
return tagdb<Tags...>{};
}
// template<class... Tags>
// constexpr auto make_tagdb(std::tuple<Tags...>) {
// return tagdb<Tags...>{};
// }
constexpr auto tags = make_tagdb(def<A, 1>(), def<B, 2>(), def<C, 3>());
int main() {
A a;
std::cout << tags.value(a) << '\n'; // Output: 1
}
The problem is that you have template base classes, all of which declare a member with the same name. The easiest fix is just to pull all the base class value functions into the derived class:
using gettag<Args>::value...;
See https://godbolt.org/z/F_Prhg
I'm playing with tuples as compile time lists. In How can I have multiple parameter packs in a variadic template? I answered myself with some code that works in both GCC and Clang, but Clang wont compile now that I've added (what I think is) perfect forwarding. It complains that As... and as... have different lengths in std::forward<As>(as).... How can this be true when As... is the type of as...? It's As&&... as in the parameters.
#include <iostream>
#include <tuple>
template < typename ... >
struct two_impl {};
// Base case
template < typename F,
typename ...Bs >
struct two_impl < F, std::tuple <>, std::tuple< Bs... > > {
void operator()(F&& f, Bs&&... bs) {
f(std::forward<Bs>(bs)...);
}
};
// Recursive case
template < typename F,
typename A,
typename ...As,
typename ...Bs >
struct two_impl < F, std::tuple< A, As... >, std::tuple< Bs...> > {
void operator()(F&& f, A&& a, As&&... as, Bs&&... bs) {
auto impl = two_impl < F, std::tuple < As&&... >, std::tuple < Bs&&..., A&& > >();
impl(std::forward<F>(f), std::forward<As>(as)..., std::forward<Bs>(bs)..., std::forward<A>(a));
}
};
template < typename F, typename ...Ts >
void two(F&& f, Ts&& ...ts) {
auto impl = two_impl< F, std::tuple < Ts... >, std::tuple <> >();
impl(std::forward<F>(f), std::forward<Ts>(ts)...);
}
struct Test {
void operator()(int i, float f, double d) {
std::cout << i << std::endl << f << std::endl << d << std::endl;
}
};
int main () {
two(Test(), 1, 1.5f, 2.1);
}
Compiling with clang -lstdc++ -std=c++0x multiple_parameter_packs.cpp
clang -lstdc++ -std=c++0x multiple_parameter_packs.cpp
multiple_parameter_packs.cpp:24:50: error: pack expansion contains parameter packs 'As' and 'as' that have different
lengths (1 vs. 2)
impl(std::forward<F>(f), std::forward<As>(as)..., std::forward<Bs>(bs)..., std::forward<A>(a));
~~ ~~ ^
multiple_parameter_packs.cpp:24:5: note: in instantiation of member function 'two_impl<Test, std::tuple<float &&,
double &&>, std::tuple<int &&> >::operator()' requested here
impl(std::forward<F>(f), std::forward<As>(as)..., std::forward<Bs>(bs)..., std::forward<A>(a));
^
multiple_parameter_packs.cpp:31:3: note: in instantiation of member function 'two_impl<Test, std::tuple<int, float,
double>, std::tuple<> >::operator()' requested here
impl(std::forward<F>(f), std::forward<Ts>(ts)...);
^
multiple_parameter_packs.cpp:41:3: note: in instantiation of function template specialization
'two<Test, int, float, double>' requested here
two(Test(), 1, 1.5f, 2.1);
^
1 error generated.
Compilation exited abnormally with code 1 at Fri Mar 23 14:25:14
This appears to be a bug in an old version of Clang. The code works fine with trunk Clang, with either libstdc++ or libc++.
$ clang++ multiple_parameter_packs.cpp -std=c++11 -stdlib=libc++
$ ./a.out
1
1.5
2.1
I don't think that this:
void operator()(F&& f, A&& a, As&&... as, Bs&&... bs)
is quite possible.
A parameter pack should be the last argument, and As&&... as is followed by another pack here.