Suppose I have a map that maps two strings into a long:
std::map<std::pair<std::string, std::string>, long> sums =
{
{{"1", "2"}, 3},
{{"3", "4"}, 7},
{{"5", "6"}, 11},
};
If I want to search in a map, it takes a std::pair<std::string, std::string>. If I receive the two strings to search by via different ways, I have to construct std::pair on the fly, which AFAIK copies the strings:
std::string one = "1";
std::string two = "2";
std::pair<std::string, std::string> myPair = std::make_pair(one, two);
std::map<std::pair<std::string, std::string>, long>::const_iterator it = sums.find(myPair);
If the map is searched A LOT, is there a way to not copy the strings to perform the search? There is no need to do a copy, it is required just by semantics of std::map.
EDIT: I am not attached to std::pair as key, it can be anything, even custom struct, as long as it is able to hold elements in the map by-value and search for them without a copy.
C++17
Since you said you are interested in a C++17 solution too, you can take advantage of std::string_view which does not allocate any memory but just holds the pointer to the actual data and the string length. Therefore, copy operation is lightweight.
The end solution would look like:
#include <iostream>
#include <string_view>
#include <map>
int main() {
std::map<std::pair<std::string_view, std::string_view>, long> sums = {
{{"1", "2"}, 3},
{{"3", "4"}, 7},
{{"5", "6"}, 11}
};
std::pair<std::string_view, std::string_view> myPair = std::make_pair("1", "2");
auto it = sums.find(myPair);
std::cout << it->second << std::endl; // output: 3
return 0;
}
C++11
If you are limited to C++11, then you can use char const* as your std::pair members but then you need to define custom compare function like:
#include <iostream>
#include <cstring>
#include <map>
struct cmp_str {
bool operator()(std::pair<char const*, char const*> const& a,
std::pair<char const*, char const*> const& b) const {
return std::strcmp(a.first, b.first) < 0;
}
};
int main() {
std::map<std::pair<char const*, char const*>, long, cmp_str> sums = {
{{"1", "2"}, 3},
{{"3", "4"}, 7},
{{"5", "6"}, 11}
};
std::pair<char const*, char const*> myPair = std::make_pair("1", "2");
auto it = sums.find(myPair);
std::cout << it->second << std::endl;
return 0;
}
C++ 11
Unfortunately you don't have std::string_view yet.
If you've received two std::string on the fly, you can always use std::move to avoid a copy if that's all that you want to do.
e.g. :
void findInMap(std::map<std::pair<std::string, std::string>, long> map,
std::string && string1, std::string && string2)
{
const auto key = std::make_pair(std::move(string1), std::move(string2));
const auto itr = map.find(key);
if(itr != map.end())
{
const auto value = itr->second;
}
}
C++11 "Solution"
If you define a map such that the Key's const char* members point to strings that are safely stored in the value, you can probably do what you want. I've got the Value inside a std::unique_ptr to ensure that the const char* in the key always points to safe memory. It's possible that the std::strings in Value return the same value from c_str before and after moves, but I don't think the standard requires this.
struct Key
{
const char* key1;
const char* key2;
bool operator<(const Key& k) {
auto cmp1 = strcmp(key1, k.key1);
if (cmp1 < 0) return true;
if (cmp1 == 0) return strcmp(key2,k.key2) < 0;
return false;
}
};
struct Value
{
const std::string key1;
const std::string key2;
long value;
};
std::map<Key, std::unique_ptr<Value>> map;
When inserting into such a monstrosity, you need to be careful though. Insert must be implemented using the map's value type - not (ever!) by using the [] operator. This is because the map's key cannot be changed, and using the [] operator default-creates the value. So after [] returns, it's too late to set the const char*'s in the key to what they need to be.
Instead you have to jump through these hoops.
std::pair<Key, std::unique_ptr<Value>> new_value;
new_value.second = std::make_unique<Value>(Value{"String1","String2"});
new_value.first = Key{new_value.second.key1.c_str(), new_value.second.key2.c_str()};
map.insert(std::move(new_value));
Lookups may then be performed without copying of strings
map.find(Key{"String1","String2"});
Whether or not this is worth it is left as an exercise for the reader.
Related
In modern C++ you can create arrays by three primary methods shown below.
// Traditional method
int array_one[] = {1, 2, 3, 4}
// Vector container
std::vector<int> array_two = {1, 2, 3, 4}
// array container
std::array<int, 4> array_three = {1, 2, 3, 4}
While each array method contains the same data, they are inherently different containers. I am writing a very simple Unit Test class with template functions to make it easier to pass multiple data types. I have an example shown below for the .hpp and .cpp calling file. The one method shown in the file takes a std::vector and compares it to another std::vector indice by indice to ensure that each value is within a certain tolerance of the other.
// main.cpp
#include <iostream>
#include <string>
#include <vector>
#include <array>
#include "unit_test.hpp"
int main(int argc, const char * argv[]) {
int array_one[] = {1, 2, 3, 4};
std::vector<int> array_two = {1, 2, 3, 4};
std::vector<float> array_four = {0.99, 1.99, 2.99, 3.99};
std::array<int, 4> array_three {1, 2, 3, 4};
std::string c ("Vector Test");
UnitTest q;
double unc = 0.1;
q.vectors_are_close(array_two, array_four, unc, c);
return 0;
}
and
#ifndef unit_test_hpp
#define unit_test_hpp
#endif /* unit_test_hpp */
#include <string>
#include <typeinfo>
#include <iostream>
#include <cmath>
class UnitTest
{
public:
template <class type1, class type2>
void vectors_are_close(const std::vector<type1> &i, const std::vector<type2> &j,
double k, std::string str);
private:
template <class type1, class type2>
void is_close(type1 &i, type2 &j, double k);
};
template <class type1, class type2>
void UnitTest::vectors_are_close(const std::vector<type1> &i, const std::vector<type2> &j,
double k, std::string str)
{
unsigned long remain;
remain = 50 - str.length();
if (i.size() != j.size()) {
std::cout << str + std::string(remain, '.') +
std::string("FAILED") << std::endl;
}
else {
try {
for (int a = 0; a < i.size(); a++){
is_close(i[a], j[a], k);
}
std::cout << str + std::string(remain, '.') +
std::string("PASSED") << std::endl;
} catch (const char* msg) {
std::cout << str + std::string(remain, '.') +
std::string("FAILED") << std::endl;
}
}
}
template <class type1, class type2>
void UnitTest::is_close(type1 &i, type2 &j, double k)
{
double percent_diff = abs((j - i) / ((i + j) / 2.0));
if (percent_diff > k) {
throw "Number not in Tolerance";
}
}
In this example the code compares two vectors; however, if I want to compare std::array containers I will have to crate a whole new function to do that, and if I want to compare two generic arrays, I will have to yet again create another function to do that. In addition, if I want to compare data in a std::array container to a std::vector container, again, I will have to create another function. I would like to create a single templated member function that I can pass any type of container to the function and have it compare it against any other type of container. In other words instead of;
void UnitTest::vectors_are_close(const std::vector<type1> &i, const std::vector<type2> & j);
I would like a simpler function such as;
void UnitTest::arrays_are_close(const type1, const type2);
where type1 and type2 do not just refer to the data in the container, but also the type of container as well. In this way I could pass a std::vector to type1 and std::array to type, or other combinations of the traditional way of creating arrays, array containers and vector containers. Is there any way to facilitate this behavior?
With a few changes to your implementation it is possible to do that:
template <class container1, class container2>
void UnitTest::vectors_are_close(const container1 &i, const container2 &j,
double k, std::string str)
{
unsigned long remain;
remain = 50 - str.length();
if (std::size(i) != std::size(j)) {
std::cout << str + std::string(remain, '.') +
std::string("FAILED") << std::endl;
}
else {
try {
for (int a = 0; a < std::size(i); a++){
is_close(i[a], j[a], k);
}
std::cout << str + std::string(remain, '.') +
std::string("PASSED") << std::endl;
} catch (const char* msg) {
std::cout << str + std::string(remain, '.') +
std::string("FAILED") << std::endl;
}
}
}
This function should work for std::vector, std::array and C-style arrays.
I am trying to use recursion to solve this problem where if i call
decimal<0,0,1>();
i should get the decimal number (4 in this case).
I am trying to use recursion with variadic templates but cannot get it to work.
Here's my code;
template<>
int decimal(){
return 0;
}
template<bool a,bool...pack>
int decimal(){
cout<<a<<"called"<<endl;
return a*2 + decimal<pack...>();
};
int main(int argc, char *argv[]){
cout<<decimal<0,0,1>()<<endl;
return 0;
}
What would be the best way to solve this?
template<typename = void>
int decimal(){
return 0;
}
template<bool a,bool...pack>
int decimal(){
cout<<a<<"called"<<endl;
return a + 2*decimal<pack...>();
};
The problem was with the recursive case, where it expects to be able to call decltype<>(). That is what I have defined in the first overload above. You can essentially ignore the typename=void, the is just necessary to allow the first one to compile.
A possible solution can be the use of a constexpr function (so you can use it's values it's value run-time, when appropriate) where the values are argument of the function.
Something like
#include <iostream>
constexpr int decimal ()
{ return 0; }
template <typename T, typename ... packT>
constexpr int decimal (T const & a, packT ... pack)
{ return a*2 + decimal(pack...); }
int main(int argc, char *argv[])
{
constexpr int val { decimal(0, 0, 1) };
static_assert( val == 2, "!");
std::cout << val << std::endl;
return 0;
}
But I obtain 2, not 4.
Are you sure that your code should return 4?
-- EDIT --
As pointed by aschepler, my example decimal() template function return "eturns twice the sum of its arguments, which is not" what do you want.
Well, with 0, 1, true and false you obtain the same; with other number, you obtain different results.
But you can modify decimal() as follows
template <typename ... packT>
constexpr int decimal (bool a, packT ... pack)
{ return a*2 + decimal(pack...); }
to avoid this problem.
This is a C++14 solution. It is mostly C++11, except for std::integral_sequence nad std::index_sequence, both of which are relatively easy to implement in C++11.
template<bool...bs>
using bools = std::integer_sequence<bool, bs...>;
template<std::uint64_t x>
using uint64 = std::integral_constant< std::uint64_t, x >;
template<std::size_t N>
constexpr uint64< ((std::uint64_t)1) << (std::uint64_t)N > bit{};
template<std::uint64_t... xs>
struct or_bits : uint64<0> {};
template<std::int64_t x0, std::int64_t... xs>
struct or_bits<x0, xs...> : uint64<x0 | or_bits<xs...>{} > {};
template<bool...bs, std::size_t...Is>
constexpr
uint64<
or_bits<
uint64<
bs?bit<Is>:std::uint64_t(0)
>{}...
>{}
>
from_binary( bools<bs...> bits, std::index_sequence<Is...> ) {
(void)bits; // suppress warning
return {};
}
template<bool...bs>
constexpr
auto from_binary( bools<bs...> bits={} )
-> decltype( from_binary( bits, std::make_index_sequence<sizeof...(bs)>{} ) )
{ return {}; }
It generates the resulting value as a type with a constexpr conversion to scalar. This is slightly more powerful than a constexpr function in its "compile-time-ness".
It assumes that the first bit is the most significant bit in the list.
You can use from_binary<1,0,1>() or from_binary( bools<1,0,1>{} ).
Live example.
This particular style of type-based programming results in code that does all of its work in its signature. The bodies consist of return {};.
Is it possible to loop over sub range using range based for loop ?
std::vector <std::string> inputs={"1","abaaaa","abc","cda"};
for (auto &it : new_vector(inputs.begin()+1, inputs.end()))
{
// …
}
You could use Boost's iterator_range:
for (auto &it : boost::make_iterator_range(inputs.begin()+1, inputs.end()))
{
cout << it << endl;
}
demo
Alternatively you could write your own wrapper.
Unfortunately, there is no such thing in the C++ standard library. However, you can define your own wrapper like this (requires at least C++ 11 - which should not be problem in 2021):
template<typename Iter>
struct range
{
Iter b, e;
Iter begin() const { return b; }
Iter end() const { return e; }
};
template<typename T>
auto slice(const T& c, std::size_t from, std::size_t to = -1) -> range<decltype(c.begin())>
{
to = (to > c.size() ? c.size() : to);
return range<decltype(c.begin())>{c.begin() + from, c.begin() + to};
}
And then you can use it:
std::vector<int> items(100);
// Iterates from 4th to 49th item
for (auto x: slice(items, 4, 50))
{
}
// Iterates from 15th to the last item
for (auto x: slice(items, 15))
{
}
tl;dr
Long story short, you #include <range/v3/view/subrange.hpp> and change your new_vector to ranges::subrange. And that's it. Demo on Compiler Explorer.
So
Given the name you imagine for this function, new_vector, maybe you think you need the entity on the right of : to be a std::vector or at least some type of container.
If that's the case, then change your mind, it's not needed. All that : wants from its "right hand side" is that it have begin and end defined on them, member or non member. For instance, this compiles and runs just fine:
struct A {};
int* begin(A);
int* end(A);
struct B {
int* begin();
int* end();
};
int main()
{
for (auto it : A{}) {}
for (auto it : B{}) {}
}
Is it possible to put the iterator of list in to set:
I wrote codes as follows :
It failed on VS2015 but run smoothly on g++
And I also tried to use std::hash to calculate a hash value of std::list::iterator
but failed again, it has no hash func for iterator.
And one can help ? Or it's impossible .....
#include <set>
#include <list>
#include <cstring>
#include <cassert>
// like std::less
struct myless
{
typedef std::list<int>::iterator first_argument_type;
typedef std::list<int>::iterator second_argument_type;
typedef bool result_type;
bool operator()(const std::list<int>::iterator& x,const std::list<int>::iterator& y) const
{
return memcmp(&x, &y, sizeof(std::list<int>::iterator)) < 0; // using memcmp
}
};
int main()
{
std::list<int> lst = {1,2,3,4,5};
std::set<std::list<int>::iterator,myless> test;
auto it = lst.begin();
test.insert(it++);
test.insert(it++);
assert(test.find(lst.begin()) != test.end()); // fail on vs 2015
auto it1 = lst.end();
auto it2 = lst.end();
assert(memcmp(&it1,&it2,sizeof(it1)) == 0); // fail on vs 2015
system("pause");
return 0;
}
Yes, you can put std::list<T>::iterator in a std::set, if you tell std::set what order they should be in. A reasonable order could be std::less<T>, i.e. you sort the iterators by the values they point to (obviously you then can't insert an std::list::end iterator). Any other order is also OK.
However, you tried to use memcmp, and that is wrong. The predicate used by set requires that equal values compare equal, and there is no guarantee that equal iterators (as defined by list::iterator::operator==) also compare equal using memcmp.
I find a way to do this as like but not for the end iterator
bool operator<(const T& x, const T& y)
{
return &*x < &*y;
}
Here's a bit of code which is supposed to filter out the elements of a map which satisfy a predicate, into a new map (MCVE-fied):
#include <algorithm>
#include <unordered_map>
#include <iostream>
using namespace std;
int main() {
unordered_map<string, int> m = { { "hello", 1 }, { "world", 2 } };
auto p = [](const decltype(m)::value_type& e) { return e.second == 2; };
const auto& m2(m);
auto m3(m2);
auto it = remove_if(m3.begin(), m3.end(), p);
m3.erase(it, m3.end());
cout << "m3.size() = " << m3.size() << endl;
return 0;
}
Compilation fails on the remove_if() line, and I get:
In file included from /usr/include/c++/4.9/utility:70:0,
from /usr/include/c++/4.9/algorithm:60,
from /tmp/b.cpp:1:
/usr/include/c++/4.9/bits/stl_pair.h: In instantiation of ‘std::pair<_T1, _T2>& std::pair<_T1, _T2>::operator=(std::pair<_T1, _T2>&&) [with _T1 = const std::basic_string<char>; _T2 = int]’:
/usr/include/c++/4.9/bits/stl_algo.h:868:23: required from ‘_ForwardIterator std::__remove_if(_ForwardIterator, _ForwardIterator, _Predicate) [with _ForwardIterator = std::__detail::_Node_iterator<std::pair<const std::basic_string<char>, int>, false, true>; _Predicate = __gnu_cxx::__ops::_Iter_pred<main()::<lambda(const value_type&)> >]’
/usr/include/c++/4.9/bits/stl_algo.h:937:47: required from ‘_FIter std::remove_if(_FIter, _FIter, _Predicate) [with _FIter = std::__detail::_Node_iterator<std::pair<const std::basic_string<char>, int>, false, true>; _Predicate = main()::<lambda(const value_type&)>]’
/tmp/b.cpp:12:48: required from here
/usr/include/c++/4.9/bits/stl_pair.h:170:8: error: passing ‘const std::basic_string<char>’ as ‘this’ argument of ‘std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ discards qualifiers [-fpermissive]
first = std::forward<first_type>(__p.first);
^
Why is this happening? remove_if should not need non-const map keys (strings in this case) - if I am not mistaken. Perhaps the autos assume somehow I want non-const iterators? If so, what do I do other tha spelling out the type (I want to avoid that since this code needs to be templated).
Your example fails even without all those intermediate variables.
unordered_map<string, int> m = { { "hello", 1 }, { "world", 2 } };
auto p = [](const decltype(m)::value_type& e) { return e.second == 2; };
auto it = remove_if(m.begin(), m.end(), p);
The code above will fail with the same errors. You can't use remove_if with associative containers because the algorithm works by moving elements that satisfy your predicate to the end of the container. But how would you reorder an unordered_map?
Write a loop for erasing elements
for(auto it = m.begin(); it != m.end();)
{
if(p(*it)) it = m.erase(it);
else ++it;
}
Or you could package that into an algorithm
template<typename Map, typename Predicate>
void map_erase_if(Map& m, Predicate const& p)
{
for(auto it = m.begin(); it != m.end();)
{
if(p(*it)) it = m.erase(it);
else ++it;
}
}
If you have a standard library implementation that implements the uniform container erasure library fundamentals extensions, then you have an algorithm similar to the one above in the std::experimental namespace.
Don't use std::remove_if for node-based containers. The algorithm attempts to permute the collection, which you either cannot do (for associative containers) or which is inefficient (for lists).
For associative containers, you'll need a normal loop:
for (auto it = m.begin(); it != m.end(); )
{
if (it->second == 2) { m.erase(it++); }
else { ++it; }
}
If you're removing from a list, use the remove member function instead, which takes a predicate.
From cppreference:
Removing is done by shifting (by means of move assignment) the elements in the range in such a way that the elements that are not to be removed appear in the beginning of the range.
You can't reorder the elements in an associative container, for the unordered_map this doesn't make sense because moving the elements to the end doesn't mean anything, they are looked up by keys anyway.