How does one do two type variadic expansion? Here is what I am trying to achieve:
#include <vector>
#include <iostream>
class test
{
public:
std::vector< std::pair< float, int > > vec;
template<typename... T1, typename... T2>
test( T1... one, T2... two )
{
(
[&](float first, int second)
{
vec.emplace_back( std::pair< float, int >( first, second ) );
std::cout << first << ", " << second << std::endl;
}( one, two ),
...
);
}
};
int main()
{
test t
{
1.f,
1,
2.f,
2,
3.f,
3
};
return 0;
}
test must be initialized exactly like in main. I'd like usage in the constructor of test to remain similar.
Here is a working concept with va_list. Unfortunately, I need to pass a count for the parameters or I need to pass a magic-number terminator (I opted for the magic-number terminator).
#include <cstdarg>
#include <iostream>
#include <vector>
constexpr int END_OBJECT = 890123; // magic number
class test {
public:
std::vector<std::pair<int, double>> vec;
enum { is_name, is_data, is_max };
test(char ch, ...) {
std::pair<int, double> buf;
va_list b;
va_start(b, ch);
for (int i = 0;; i++) {
auto is = i % is_max;
if (is == is_name) {
if ( (buf.first = va_arg(b, int)) == END_OBJECT )
break;
} else if (is == is_data) {
buf.second = va_arg(b, double);
vec.emplace_back(buf);
}
}
va_end(b);
std::cout << ch << std::endl;
for (auto &x : vec)
std::cout << '\t' << x.first << ", " << x.second << std::endl;
}
};
int main() {
test t
{
'x',
1,
2.0,
3,
4.0,
5,
6.0,
END_OBJECT
};
return 0;
}
I'd like a more modern version of this using pack-expansion.
It's funny how this is basically FizzBuzz with template arguments and it's actually a nice challenge.
The easiest way in C++14 I could come up with is to use std::index_sequence.
https://godbolt.org/z/dm3F9u
#include <vector>
#include <utility>
#include <tuple>
template <typename... TArgs, size_t... Is>
std::vector<std::pair<float, int>> pair_off(std::tuple<TArgs...> args, std::index_sequence<Is...>) {
return std::vector<std::pair<float, int>> { std::make_pair(std::get<(Is << 1)>(args), std::get<((Is << 1) + 1)>(args))... };
}
template <typename... TArgs>
std::vector<std::pair<float, int>> pair_off(TArgs&&... args) {
return pair_off(std::forward_as_tuple(std::forward<TArgs>(args)...), std::make_index_sequence<(sizeof...(TArgs) >> 1)>{});
}
std::vector<std::pair<float, int>> test() {
return pair_off(1.1f, 1, 2.2f, 2, 3.3f, 3);
}
Basically, first you pack the arguments into a tuple. Then you make an index sequence half the size of the argument list. Then you expand that index sequence, passing it into std::get.
What that does is the template equivalent of:
for (int i=0; i<list.size()/2; i++) {
output.push_back( std::make_pair(list[i*2], list[i*2+1]) );
}
Related
I want to get a matrix from two parameter packs like the following:
template < typename T1, typename T2 > struct Multi{};
template < int ... n > struct N{};
void Print( int n ){ std::cout << n << std::endl; }
template < int ... n1, int ... n2 >
struct Multi< N<n1...>, N<n2...>>
{
Multi()
{
using expander = int[];
// No idea which syntax should be used here:
expander{ 0,((void)Print(n1...*n2),0)... };
}
};
int main()
{
Multi< N<1,2,3,4>, N< 10,20> >{};
}
The result should be
10 20 30 40 20 40 60 80
How can I do this?
No need to use the dummy arrays when you have fold expressions.
The naive (Print(n1 * n2), ...); wouldn't work (it expects the packs to have the same size, and would print N numbers instead of N2).
You need two nested fold expressions. In the inner one, you can prevent one of the packs from being expanded by passing it as a lambda parameter.
([](int n){(Print(n1 * n), ...);}(n2), ...);
This is not single expression, but you can expand it and use for loop
template < int ... n1, int ... n2 >
struct Multi< N<n1...>, N<n2...>>
{
Multi()
{
for(auto j : {n2...})
for(auto i : {n1...})
std::cout << i*j << '\n';
}
};
WandBox
I kind of assume that the output in your code is to check the compile time evaluation, since the output to std::cout only works at runtime.
Another option is not to use structs but to use constexpr functions,
they look more like regular c++ code. And you van validate the correctness at compile time using static_asserts. I did add some output at the end of my example
live demo here : https://onlinegdb.com/iNrqezstg
#include <array>
#include <iostream>
template<int... n>
constexpr auto array()
{
return std::array<int,sizeof...(n)>{n...};
};
template<std::size_t N, std::size_t M>
constexpr auto multiply(const std::array<int, N>& arr1, const std::array<int, M>& arr2)
{
std::array<int, N* M> result{};
std::size_t index{ 0 };
for (std::size_t n = 0; n < N; n++)
{
for (std::size_t m = 0; m < M; m++)
{
result[index] = arr1[n] * arr2[m];
++index;
}
}
return result;
}
template<typename container_t>
void show(const char* msg, const container_t& values)
{
std::cout << msg << " : ";
bool comma{ false };
for (const auto& value : values)
{
if (comma) std::cout << ", ";
std::cout << value;
comma = true;
}
std::cout << "\n";
}
int main()
{
constexpr auto arr1 = array<1, 2, 3, 4>();
constexpr auto arr2 = array<10, 20>();
constexpr auto result = multiply(arr1, arr2);
static_assert(arr1[0] == 1, "");
static_assert(arr2[1] == 20, "");
static_assert(result[0] == 10, "");
static_assert(result[1] == 20, "");
static_assert(result[6] == 40, "");
show("arr1", arr1);
show("arr2", arr2);
show("result", result);
return 0;
}
So basically, I want to create a function like this:
total_sum(1, 2, 5, 4, 2) // return 14
total_sum(5, 6, 2) // return 13
One way that I can use is ellipsis, something like this:
#include <cstdarg>
int total_sum(int count, ...)
{
int sum{0};
va_list list;
va_start(list, count);
for (int arg{0}; arg < count; ++arg)
{
sum += va_arg(list, int);
}
va_end(list);
return sum;
}
But when I call total_sum(), I have to provide an extra argument. So in the example, I have to call:
total_sum(5, 1, 2, 5, 4, 2);
total_sum(3, 5, 6, 2);
which I don't really like. Also, ellipsis is really prone to error, so I want to stay away from them.
Another way I can think of is using some container:
#include <vector>
int total_sum(std::vector<int> values)
{
int sum{0};
for(const auto &i : values)
{
sum += i;
}
return sum;
}
and I call it like:
total_sum({1, 2, 5, 4, 2});
total_sum({3, 5, 6, 2});
But, I want to not have those curly braces, so what can I do? Is there some C++ feature that allows me to do this?
Some relevant links: restrict variadic template arguments, fold expression, parameter packs and a
C++11 "equivalent" of fold expressions
Use C++17 fold expression:
template<class... Args>
constexpr auto total_sum(const Args&... args) {
return (args + ... + 0);
}
static_assert(total_sum(1, 2, 5, 4, 2) == 14);
static_assert(total_sum(3, 5, 6, 2) == 16);
In C++11 and newer you can use template parameter packs to create a recursive implementation, like this:
#include <iostream>
// base function
int total_sum()
{
return 0;
}
// recursive variadic function
template<typename T, typename... Targs>
int total_sum(T firstValue, Targs... Fargs)
{
return firstValue + total_sum(Fargs...);
}
int main(int, char **)
{
int s = total_sum(1, 5, 10, 20);
std::cout << "sum is: " << s << std::endl;
return 0;
}
Running the above program produces this output:
sum is: 36
All of the code below is is based on this article.
You can also do this kind-of C++11 "equivalent" of fold expressions:
#include <iostream>
#include <algorithm>
#include <utility>
#include <type_traits>
/*
Overload a total_sum() function with no arguments.
so it works when calling total_sum() with no argument.
*/
int total_sum()
{
return 0;
}
template<typename ... I>
int total_sum(const I &... i)
{
int result{};
static_cast<void>(std::initializer_list<int>{(result += i, 0)... });
return result;
}
int main()
{
std::cout << total_sum() << '\n';
std::cout << total_sum(1, 2, 5, 4, 2) << '\n';
std::cout << total_sum(5, 6, 2) << '\n';
}
See this online!
This one can add anything that can convert to an add-able value:
#include <iostream>
#include <algorithm>
#include <utility>
#include <type_traits>
int total_sum()
{
return 0;
}
template<typename ... V>
typename std::common_type<V...>::type total_sum(const V &... v)
{
typename std::common_type<V...>::type result = {};
static_cast<void>(std::initializer_list<int>{ (result += v, 0)... });
return result;
}
int main()
{
std::cout << total_sum() << '\n';
std::cout << total_sum(5, 6, 2) << '\n';
}
See this online!
The code above can be cleaner using C++14:
#include <iostream>
#include <algorithm>
#include <utility>
#include <type_traits>
int total_sum()
{
return 0;
}
template<typename ... V>
auto total_sum(const V &... v) {
std::common_type_t<V...> result = {};
static_cast<void>(std::initializer_list<int>{ (result += v, 0)... });
return result;
}
int main()
{
std::cout << total_sum() << '\n';
std::cout << total_sum(5, 6, 2) << '\n';
}
See this online!
I am trying to solve a question on leetcode which is finding the top k frequent elements. I think my code is correct but the output for a test case is failing.
Input: [ 4,1,-1,2,-1,2,3]
K=2
My answer comes out to be {1,-1} but the expected is {-1,2}. I am not sure where am i getting wrong.
struct myComp{
constexpr bool operator()(pair<int,int> & a,pair<int,int> &b)
const noexcept
{
if(a.second==b.second)
{
return a.first<b.first;
}
return a.second<b.second;
}
};
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int> mp;
for(int i=0;i<nums.size();i++)
{
mp[nums[i]]++;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,myComp> minheap;
for(auto x:mp)
{
minheap.push(make_pair(x.second,x.first));
if(minheap.size()>k)
{
minheap.pop();
}
}
vector<int> x;
while(minheap.size()>0)
{
x.push_back(minheap.top().second);
minheap.pop();
}
return x;
link: https://leetcode.com/problems/top-k-frequent-elements
In the minheap, pairs of <frequency, element> are being pushed. Since we want to sort these pairs on basis of frequency, we need to compare on the basis of frequency only.
Let's say there are two pairs a and b. Then for normal sorting, the comparison would be :
a.first < b.first;
And for reverse sorting, the comparison would be :
a.first > b.first;
In case of min-heap, we need reverse sorting. Hence, the following comparator makes your code pass all the test cases :
struct myComp
{
constexpr bool operator()(pair<int,int> & a,pair<int,int> &b)
const noexcept
{
return a.first > b.first;
}
};
There are several issues with your code.
Obviously there is somewhere using namespace std; in your code. This should be avoided. You will find many posts here on SO explaining, why it this should not be done.
Then we need to qualify all elements from the std library with std::, which makes the scope very clear.
Next: You do not need your own sorting function. Since you insert the elements from the pair in swapped order into the std::priority_queue, the sorting criteria is valid for the counter part, not for the key value. So, your sorting function was anyway wrong, because it was sorting accodring to "second" and not to "first". But if we have a standard sorting, we do not need a special sorting algorithm. A std::pair has a less-than operator. So, the definition can be simply:
std::priority_queue<std::pair<int, int>> minheap;
Then, your if statement
if(minheap.size()>k)
{
minheap.pop();
}
is wrong. You will allow only k values to be inserted. And this are not necessarily the biggest ones. So, you need to insert all values from the std::unordered map. And then they are sorted.
With some cosmetic changes the code will look like the below:
#include <iostream>
#include <utility>
#include <unordered_map>
#include <vector>
#include <queue>
std::vector<int> topKFrequent(std::vector<int>& nums, size_t k) {
std::unordered_map<int, int> mp;
for (size_t i = 0; i < nums.size(); i++)
{
mp[nums[i]]++;
}
std::priority_queue<std::pair<int, int>> minheap;
for (auto x : mp)
{
minheap.push(std::make_pair(x.second, x.first));
}
std::vector<int> x;
for (size_t i{}; i< k; ++i)
{
x.push_back(minheap.top().second);
minheap.pop();
}
return x;
}
int main() {
std::vector data{ 4,1,-1,2,-1,2,3 };
std::vector result = topKFrequent(data, 2);
for (const int i : result) std::cout << i << ' '; std::cout << '\n';
return 0;
}
An additional solution
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <utility>
auto topKFrequent(std::vector<int>& nums, size_t k) {
// Count occurences
std::unordered_map<int, size_t> counter{};
for (const int& i : nums) counter[i]++;
// For storing the top k
std::vector<std::pair<int, size_t>> top(k);
// Get top k
std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(),
[](const std::pair<int, size_t >& p1, const std::pair<int, size_t>& p2) { return p1.second > p2.second; });
return top;
}
// Test code
int main() {
std::vector data{ 4,1,-1,2,-1,2,3 };
for (const auto& p : topKFrequent(data, 2))
std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
return 0;
}
And of course, we do have also the universal solution for any kind of iterable container. Including the definition for type traits using SFINAE and checking for the correct template parameter.
#include <iostream>
#include <utility>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <iterator>
#include <type_traits>
// Helper for type trait We want to identify an iterable container ----------------------------------------------------
template <typename Container>
auto isIterableHelper(int) -> decltype (
std::begin(std::declval<Container&>()) != std::end(std::declval<Container&>()), // begin/end and operator !=
++std::declval<decltype(std::begin(std::declval<Container&>()))&>(), // operator ++
void(*std::begin(std::declval<Container&>())), // operator*
void(), // Handle potential operator ,
std::true_type{});
template <typename T>
std::false_type isIterableHelper(...);
// The type trait -----------------------------------------------------------------------------------------------------
template <typename Container>
using is_iterable = decltype(isIterableHelper<Container>(0));
// Some Alias names for later easier reading --------------------------------------------------------------------------
template <typename Container>
using ValueType = std::decay_t<decltype(*std::begin(std::declval<Container&>()))>;
template <typename Container>
using Pair = std::pair<ValueType<Container>, size_t>;
template <typename Container>
using Counter = std::unordered_map<ValueType<Container>, size_t>;
// Function to get the k most frequent elements used in any Container ------------------------------------------------
template <class Container>
auto topKFrequent(const Container& data, size_t k) {
if constexpr (is_iterable<Container>::value) {
// Count all occurences of data
Counter<Container> counter{};
for (const auto& d : data) counter[d]++;
// For storing the top k
std::vector<Pair<Container>> top(k);
// Get top k
std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(),
[](const std::pair<int, size_t >& p1, const std::pair<int, size_t>& p2) { return p1.second > p2.second; });
return top;
}
else
return data;
}
int main() {
std::vector testVector{ 1,2,2,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,6,7 };
for (const auto& p : topKFrequent(testVector, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
double cStyleArray[] = { 1.1, 2.2, 2.2, 3.3, 3.3, 3.3 };
for (const auto& p : topKFrequent(cStyleArray, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
std::string s{"abbcccddddeeeeeffffffggggggg"};
for (const auto& p : topKFrequent(s, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
double value = 12.34;
std::cout << topKFrequent(value,2) << "\n";
return 0;
}
Developed and tested with Microsoft Visual Studio Community 2019, Version 16.8.2.
Additionally compiled and tested with clang11.0 and gcc10.2
Language: C++17
I created a foreach() function, and it should print values of an array, but it tells me this:
no matching function for call to 'foreach(std::array<int, 4>&, void(&)(int))'
And also:
mismatched types 'unsigned int' and 'long unsigned int'
But when I try to use vectors instead of arrays, or on line 11 use template<unsigned int N> instead of unsigned int, if I use long unsigned int, it works fine.
So, why do I need to use long unsigned int?
And what does the "no matching function" error mean with arrays?
#include<iostream>
#include<string>
#include<array>
typedef void(*func)(int);
void print(int value) {
std::cout << "value is : " << value << std::endl;
}
template<unsigned int N>
void foreach(std::array<int, N>& values, func print) {
int value;
for(int i = 0; i < values.size(); i++) {
value = values[i];
print(value);
}
}
int main() {
std::array<int, 4> arr = { 0, 1, 2, 3 };
foreach(arr, print);
return 0;
}
With vectors:
#include<iostream>
#include<string>
#include<vector>
typedef void(*func)(int);
void print(int value) {
std::cout << "value is : " << value << std::endl;
}
void foreach(std::vector<int>& values, func print) {
int value;
for(int i = 0; i < values.size(); i++) {
value = values[i];
print(value);
}
}
int main() {
std::vector<int> v = { 0, 1, 2, 3 };
foreach(v, print);
return 0;
}
The template for std::array does not take an unsigned int, it takes a std::size_t, which may or may not (probably not) be defined as unsigned int:
template<size_t N>
void foreach(std::array<int, N>& values, func print);
A better option is to make your function be container-agnostic instead, by passing it iterators instead of the actual container, eg:
#include <iostream>
#include <string>
#include <array>
#include <vector>
typedef void(*func)(int);
void print(int value) {
std::cout << "value is : " << value << std::endl;
}
template<typename Iter>
void foreach(Iter begin, Iter end, func print) {
while (begin != end) {
print(*begin);
++begin;
}
}
int main() {
std::array<int, 4> arr = { 0, 1, 2, 3 };
foreach(arr.begin(), arr.end(), print);
std::vector<int> v = { 0, 1, 2, 3 };
foreach(v.begin(), v.end(), print);
return 0;
}
Not only does this allow the function to work with multiple containers, but also with different element types, if you change the print parameter to be a template as well, eg:
#include <iostream>
#include <string>
#include <array>
#include <vector>
void printInt(int value) {
std::cout << "value is : " << value << std::endl;
}
void printStr(const std::string &value) {
std::cout << "value is : " << value << std::endl;
}
template<typename Iter, typename Callable>
void foreach(Iter begin, Iter end, Callable print) {
while (begin != end) {
print(*begin);
++begin;
}
}
int main() {
std::array<int, 4> arr = { 0, 1, 2, 3 };
foreach(arr.begin(), arr.end(), printInt);
std::vector<std::string> v = { "hello", "world", "joe", "smoe" };
foreach(v.begin(), v.end(), printStr);
return 0;
}
This is the exact strategy that standard algorithms use, such as std::for_each() (which you should be using instead of writing your own) 1, eg:
#include <iostream>
#include <string>
#include <array>
#include <vector>
#include <algorithm>
int main() {
std::array<int, 4> arr = { 0, 1, 2, 3 };
std::for_each(arr.begin(), arr.end(),
[](int value) { std::cout << "value is : " << value << std::endl; }
);
std::vector<std::string> v = { "hello", "world", "joe", "smoe" };
std::for_each(v.begin(), v.end(),
[](const std::string &value) { std::cout << "value is : " << value << std::endl; }
);
return 0;
}
1: C++20 introduced a new Ranges library that has algorithms to act on whole containers.
Because the second template parameter for std::array is std::size_t, not unsigned int. Compiler cannot infer the convertion between types in template function. And it just happens that std::size_t in your compiler is a typedef on long unsigned int, so that's what it suggests.
You can make it work in two ways:
Change the template type to std::size_t
Provide template parameter explicitly when calling:
foreach<4>(v, print);
template<typename T,int nSize>
T Sum(T (&parr)[nSize])
{
T sum=0;
for(int i = 0; i < nSize ;++i)
{
sum += parr[i];
}
return sum;
}
int _tmain(int argc, _TCHAR* argv[])
{
int nArr[] = {1,2,3,4};
int nSum = Sum(nArr);
std::cout<<"Sum :"<<nSum;
}
Can std::vector be used instead of array.Or can array be replaced by any of the stl containers?
Can std::vector be used instead of array.Or can array be replaced by
any of the stl containers?
No. It is not possible as they are different in their types. But you can generalize the given function in the following way.
Make a template function taking the begin and end iterators of the container. Then using std::accumulate, sum the elements up, which will work for any sequence containers as well as the arrays:
Following is an example code: (See live online)
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <numeric> // std::accumulate
#include <iterator> // std::iterator_traits, std::cbegin, std::cend
template<typename Iterator>
constexpr auto Sum(Iterator begin, const Iterator end) -> typename std::iterator_traits<Iterator>::value_type
{
using RetType = typename std::iterator_traits<Iterator>::value_type;
return std::accumulate(begin, end, RetType{});
}
int main()
{
int nArr[] = { 1,2,3,4 };
std::vector<int> vec{ 1,2,3,4 };
std::list<int> list{ 1,2,3,4 };
// now you can
std::cout << "Sum of array: " << Sum(std::cbegin(nArr), std::cend(nArr)) << "\n";
std::cout << "Sum of vec: " << Sum(std::cbegin(vec), std::cend(vec)) << "\n";
std::cout << "Sum of list: " << Sum(std::cbegin(list), std::cend(list)) << "\n";
}
Output:
Sum of array: 10
Sum of vec: 10
Sum of list: 10
template<typename T, int nSize>
T sum(std::array<T, nSize> const&);
would be the equivalent signature for std::array. As you see, signature differs already. Trying to do the same for std::vector is bound to fail:
template<typename T, int nSize>
T sum(std::vector<T> const&);
How would you be able to know at compile time already how many elements will reside in the vector??? You simply cannot. Even if you specified nSize in code explicitly, e. g. sum<std::vector<int>, 7>, the function then would always try to iterate over exactly seven elements, resulting in undefined behaviour if there are less and not counting the surplus ones if there are more...
The typical way to go is using begin and end iterators, just as the standard library does, too, for all of its algorithms:
template <typename Iterator>
auto sum(Iterator begin, Iterator end) -> std::remove_reference_t<decltype(*begin)>
{
using type = decltype(sum(begin, end)); // just not wanting to repeat all
// that remove_reference stuff...
type s = type();
for( ; begin != end; ++begin)
{
s += *begin;
}
return s;
}
You additionally could, based on this function, provide a generic overload for arbitrary containers then:
template <typename Container>
auto sum(Container const& c)
{
using std::begin;
using std::end;
return sum(begin(c), end(c));
}
If your compiler supports C++ 17 then you can write a single function with using if constexpr statement.
For example
#include <iostream>
#include <vector>
template<typename T>
auto Sum( const T &container )
{
if constexpr( std::is_array_v<std::remove_reference_t<T>> )
{
std::remove_extent_t<T> sum = 0;
for ( const auto &item : container )
{
sum += item;
}
return sum;
}
else
{
typename T::value_type sum = 0;
for ( const auto &item : container )
{
sum += item;
}
return sum;
}
}
int main()
{
int nArr[] = { 1, 2, 3, 4 };
int nSum = Sum( nArr );
std::cout << "Sum :"<<nSum << '\n';;
std::vector<int> v = { 1, 2, 3, 4 };
nSum = Sum( v );
std::cout << "Sum :"<<nSum << '\n';;
}
The program output is
Sum :10
Sum :10
However it is better to split the function into two functions: one for arrays and other for standard containers.
#include <iostream>
#include <vector>
template<typename T, size_t N>
auto Sum( const T ( &a )[N] )
{
T sum = 0;
for ( const auto &item : a )
{
sum += item;
}
return sum;
}
template<typename T>
auto Sum( const T &container )
{
typename T::value_type sum = 0;
for ( const auto &item : container )
{
sum += item;
}
return sum;
}
int main()
{
int nArr[] = { 1, 2, 3, 4 };
int nSum = Sum( nArr );
std::cout << "Sum :"<<nSum << '\n';;
std::vector<int> v = { 1, 2, 3, 4 };
nSum = Sum( v );
std::cout << "Sum :"<<nSum << '\n';;
}