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 can't seem to figure out why the following code doesn't work:
#include <array>
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(const std::array<int, s>& arr) {
a<arr.size()>(); // error: no matching function for call to 'a'
}
int main() {
const std::array<int, 2> arr {{0, 0}};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
GCC fails with the following:
test.cpp: In instantiation of ‘void b(const std::array<int, s>&) [with long unsigned int s = 2]’:
test.cpp:13:22: required from here
test.cpp:6:18: error: no matching function for call to ‘a<(& arr)->std::array<int, 2>::size()>()’
6 | a<arr.size()>(); // Doesn't
| ~~~~~~~~~~~~~^~
test.cpp:3:37: note: candidate: ‘template<long unsigned int s> void a()’
3 | template <long unsigned int s> void a() {}
| ^
test.cpp:3:37: note: template argument deduction/substitution failed:
test.cpp:6:18: error: ‘arr’ is not a constant expression
6 | a<arr.size()>(); // Doesn't
| ~~~~~~~~~~~~~^~
test.cpp:6:15: note: in template argument for type ‘long unsigned int’
6 | a<arr.size()>(); // Doesn't
| ~~~~~~~~^~
I assume the ‘arr’ is not a constant expression part is most relevant, but I don't understand why the same line works in main() (is it a constant expression there?), and why passing arr as a const copy (rather than a reference) also resolves the issue.
PS: I know that I can just use a<s>();, but I'm just trying to figure out what this error means.
Maybe in C++2X, it will work with consteval functitons.
For non-reference argument it works https://godbolt.org/z/Wx9va54z5.
I think it is fine to pass std::array of built-ins by value in general anyway.
(This works in GCC and clang.)
#include <array>
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(std::array<int, s> arr) {
a<arr.size()>(); // ok
}
int main() {
const std::array<int, 2> arr = {};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
To answer the question in your comment #acumandr, yes, a static and constexpr size() member function in std::array would work(!), even for const& argument. https://godbolt.org/z/anP1e9qEr
CORRECTION: This is correct only for GCC, in clang doesn't work https://godbolt.org/z/65d5G9Yfo , Thanks #IlCapitano
#include <array>
template<class T, std::size_t D>
struct MyArray{
static constexpr std::size_t size(){return D;}
};
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(MyArray<int, s> const& arr) {
a<arr.size()>(); // ok
}
int main() {
const MyArray<int, 2> arr = {};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
which makes even more puzzling why std::array doesn't have a static (and constexpr) size member.
2.5) CORRECTION: This is correct only for GCC, in clang doesn't work https://godbolt.org/z/65d5G9Yfo , Thanks #IlCapitano
In clang, a static funciton works but not passing the instance, which kind of defeats the purpose:
https://godbolt.org/z/En13aEWPz
#include <array>
template<class T, std::size_t D>
struct MyArray{
static constexpr std::size_t size(){return D;}
};
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(MyArray<int, s> const& arr) {
a<std::decay_t<decltype(arr)>::size()>(); // error: no matching function for call to 'a'
}
int main() {
const MyArray<int, 2> arr = {};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
using std::tuple_size<decltype(...)> (or s itself) is a good workaround https://godbolt.org/z/K9xxKa1Px
#include <array>
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(std::array<int, s> arr) {
a<std::tuple_size<decltype(arr)>::value /*or just s*/>(); // ok
}
int main() {
const std::array<int, 2> arr = {};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
This program defines a simple container for an array of std::unique_ptr<unsigned int>.
#include <memory>
#include <array>
#include <type_traits>
template <unsigned int dim>
class container {
template <std::size_t... I>
container(std::array<unsigned int, dim> Ls, std::index_sequence<I...>) : container(std::get<I>(Ls)...) { }
public:
const std::array<std::unique_ptr<unsigned int>, dim> data;
template <typename... UInts, class = std::enable_if_t<(sizeof...(UInts) == dim) && (std::is_same_v<UInts, unsigned int> && ...)>>
container(UInts... Ls) : data{ std::make_unique<unsigned int>(Ls)...} { }
template <typename Indices = std::make_index_sequence<dim>>
container(std::array<unsigned int, dim> Ls) : container(Ls, Indices{}) { }
};
int main()
{
unsigned int x = 1;
unsigned int y = 2;
container<2> a({x,y});
return 0;
}
The compilation with gcc 8.2.1 fails however, with the following error.
$ g++ -Wall -pedantic -std=c++17 -o test test.cpp
test.cpp: In function ‘int main()’:
test.cpp:25:23: error: call of overloaded ‘container(<brace-enclosed initializer list>)’ is ambiguous
container<2> a({x,y});
^
test.cpp:17:3: note: candidate: ‘container<dim>::container(std::array<unsigned int, dim>) [with Indices = std::integer_sequence<long unsigned int, 0, 1>; unsigned int dim = 2]’
container(std::array<unsigned int, dim> Ls) : container(Ls, Indices{}) { }
^~~~~~~~~
test.cpp:6:7: note: candidate: ‘container<2>::container(const container<2>&)’ <deleted>
class container {
^~~~~~~~~
test.cpp:6:7: note: candidate: ‘container<2>::container(container<2>&&)’ <deleted>
make: *** [Makefile:3: test] Error 1
Indeed, the copy constructor is deleted because of the presence of the array of std::unique_ptr (but still participates to the overload resolution).
Strangely, on gcc 7.3.0 the above code compiles with no errors or warnings.
Also, defining a simplified container, having a copyable array of unsigned int, as in the following program, no error occurs with both gcc 8.2.1 and gcc 7.3.0
#include <array>
#include <type_traits>
template <unsigned int dim>
class container_simple {
template <std::size_t... I>
container_simple(std::array<unsigned int, dim> Ls, std::index_sequence<I...>) : container_simple(std::get<I>(Ls)...) { }
public:
const std::array<unsigned int, dim> data;
template <typename... UInts, class = std::enable_if_t<(sizeof...(UInts) == dim) && (std::is_same_v<UInts, unsigned int> && ...)>>
container_simple(UInts... Ls) : data{ Ls...} { }
template <typename Indices = std::make_index_sequence<dim>>
container_simple(std::array<unsigned int, dim> Ls) : container_simple(Ls, Indices{}) { }
};
int main()
{
unsigned int x = 1;
unsigned int y = 2;
container_simple<2> a({x,y});
return 0;
}
Is it a compiler bug? If not, where is the ambiguity?
The following code compiles:
#include "List.h"
//namespace prototypeInd{namespace templates{ //Uncomment for error
template <size_t N, typename Lambda>
inline void static_for(const Lambda& f,std::integral_constant<size_t,N>) {
static_for(f, std::integral_constant<size_t,N-1>());
f(std::integral_constant<size_t,N-1>());
}
template <typename Lambda>
inline void static_for(const Lambda& f, std::integral_constant<size_t,1>) {
f(std::integral_constant<size_t,0>());
}
//}} //Uncomment for error
//using namespace prototypeInd::templates; //Uncomment for error
template<size_t N>
using ic = std::integral_constant<size_t,N>;
typedef List<ic<0>,ic<1>,ic<2>,ic<3>,ic<4>,ic<5>,ic<6>,ic<7> > list;
int main(int argc,char** args){
static_for([](auto i){ std::cout << list::IndexOf<i>() << std::endl; },list::SizeOf());
}
But as soon as you uncomment the marked lines it gives a huge whopper of an error.
Here is List.h:
template<class First, class... Rest>
struct SizeOf_ : std::integral_constant<size_t, SizeOf_<Rest...>() + 1>{};
template<class First>
struct SizeOf_<First> : std::integral_constant<size_t,1> { };
template<size_t index, class First, class... Rest>
struct Index_{
static_assert(index < SizeOf_<First,Rest...>(), "Index is outside of bounds");
typedef typename Index_<index - 1, Rest...>::value value;
};
template<class First, class... Rest>
struct Index_<0,First, Rest...>{
typedef First value;
};
template<class First_t, class... Rest_t>
struct List{
template<size_t i>
using IndexOf = typename Index_<i,First_t,Rest_t...>::value;
typedef SizeOf_<First_t, Rest_t...> SizeOf;
};
This seems inexplicable....A namespace should not change the function of the code?!?!?!? (or should it?).
I am using g++ with std=c++14....Any help is appreciated
Why does having a namespace change anything?
When Alf compiles this code with g++ 5.1.0 and removes the out-commenting, that compiler reports as first error that
foo.cpp:11:5: error: static assertion failed: Index is outside of bounds
static_assert(index < SizeOf_<First,Rest...>(), "Index is outside of bounds");
The complete diagnostics avalanche for that first error:
foo.cpp: In instantiation of 'struct Index_<18446744073709551615ull, std::integral_constant<long long unsigned int, 0ull>, std::integral_constant<long long unsigned int, 1ull>, std::integral_constant<long long unsigned int, 2ull>, std::integral_constant<long long unsigned int, 3ull>, std::integral_constant<long long unsigned int, 4ull>, std::integral_constant<long long unsigned int, 5ull>, std::integral_constant<long long unsigned int, 6ull>, std::integral_constant<long long unsigned int, 7ull> >':
foo.cpp:21:64: required by substitution of 'template<class First_t, class ... Rest_t> template<long long unsigned int i> using IndexOf = typename Index_<i, First_t, Rest_t ...>::value [with long long unsigned int i = i; First_t = std::integral_constant<long long unsigned int, 0ull>; Rest_t = {std::integral_constant<long long unsigned int, 1ull>, std::integral_constant<long long unsigned int, 2ull>, std::integral_constant<long long unsigned int, 3ull>, std::integral_constant<long long unsigned int, 4ull>, std::integral_constant<long long unsigned int, 5ull>, std::integral_constant<long long unsigned int, 6ull>, std::integral_constant<long long unsigned int, 7ull>}]'
foo.cpp:52:38: required from 'main(int, char**):: [with auto:1 = std::integral_constant<long long unsigned int, 18446744073709551615ull>]'
foo.cpp:33:15: recursively required from 'void prototypeInd::templates::static_for(const Lambda&, std::integral_constant<long long unsigned int, N>) [with long long unsigned int N = 7ull; Lambda = main(int, char**)::]'
foo.cpp:33:15: required from 'void prototypeInd::templates::static_for(const Lambda&, std::integral_constant<long long unsigned int, N>) [with long long unsigned int N = 8ull; Lambda = main(int, char**)::]'
foo.cpp:52:90: required from here
foo.cpp:11:5: error: static assertion failed: Index is outside of bounds
static_assert(index < SizeOf_<First,Rest...>(), "Index is outside of bounds");
^