Here's what I have:
Demo
#include <cstdio>
#include <memory_resource>
#include <memory>
#include <string_view>
#include <array>
#include <utility> /* declval */
struct msg_outbound_t
{
std::string_view hello_ = "Hello World!";
};
// Default deleter - works
// using msg_handle_t = std::unique_ptr<msg_outbound_t>;
// Custom deleter - doesn't work
using allocator_t = std::pmr::polymorphic_allocator<std::byte>;
auto pmr_deleter(allocator_t allocator) {
return [allocator](msg_outbound_t* p) mutable { allocator.delete_object(p); };
}
using msg_handle_t = std::unique_ptr<msg_outbound_t, decltype(pmr_deleter(std::declval<allocator_t>()))>;
int main()
{
std::array<msg_handle_t, 8> myarray;
}
It doesn't work as it seems that unique_ptrs with custom deleters can't be default constructed? Is there a workaround to create an array of them?
Errors:
<source>:31:33: error: use of deleted function 'std::array<std::unique_ptr<msg_outbound_t, pmr_deleter(allocator_t)::<lambda(msg_outbound_t*)> >, 8>::array()'
31 | std::array<msg_handle_t, 8> myarray;
| ^~~~~~~
it seems that unique_ptrs with custom deleters can't be default constructed?
Yes, but you'll have to supply a class that it can instantiate as a template parameter:
using allocator_t = std::pmr::polymorphic_allocator<std::byte>;
struct pmr_deleter {
void operator()(msg_outbound_t * p) {
allocator.delete_object(p);
}
allocator_t allocator;
};
using msg_handle_t = std::unique_ptr<msg_outbound_t, pmr_deleter>;
Demo
Related
I am able to do this:
std::vector<int> vec = { 1, 2, 3, 4, 5 };
But I am not able to do this:
std::vector<const type_info&> ClassBlackList = { typeid(Class1), typeid(Class2) };
compiler says pointer to reference is illegal
or
std::vector<const type_info> ClassBlackList = { typeid(Class1), typeid(Class2) };
compiler says Error C2338 The C++ Standard forbids containers of const elements because allocator is ill-formed.
or
std::vector<type_info> ClassBlackList = { typeid(Class1), typeid(Class2) };
Compiler says:
Error C2280 'type_info::type_info(const type_info &)': attempting to reference a deleted function
I am able not able to do push_back either.
What is the solution to have a vector or list of type_info?
You can use pointers
std::vector<const std::type_info*> v = { &typeid(Class1), &typeid(Class2) };
This is valid because typeid returns a reference to an object with static storage duration.
You cannot have a vector of references, for several fundamental reasons. C++ simply does not work this way. You can, however, employ std::reference_wrapper to get pretty much the same result:
#include <functional>
#include <vector>
#include <typeinfo>
class A {
};
int main()
{
std::vector<std::reference_wrapper<const std::type_info>> avec;
auto &t=typeid(A);
avec.push_back(t);
const std::type_info &i=avec[0];
return 0;
}
You can't have arrays of references so you could wrap them in std::reference_wrappers:
#include <functional>
#include <typeinfo>
#include <vector>
std::vector<std::reference_wrapper<const std::type_info>> ClassBlackList = {
typeid(Class1),
typeid(Class2)
};
The name ClassBlackList implies that you will search it a lot and also that the elements in the list are to be unique. In that case, you may want to use a std::set instead.
Example:
#include <functional>
#include <iostream>
#include <typeinfo>
#include <set>
struct Class1 {};
struct Class2 {};
struct Class3 {};
struct comp { // a functor to compare reference wrapped type_info's
std::size_t operator()(const std::reference_wrapper<const std::type_info>& lhs,
const std::reference_wrapper<const std::type_info>& rhs) const
{
return std::less<const std::type_info*>{}(&lhs.get(), &rhs.get());
}
};
int main() {
std::set<std::reference_wrapper<const std::type_info>, comp> ClassBlackList = {
typeid(Class1),
typeid(Class2)
};
// try to insert typeid(Class3) twice, it only succeeds the first time
auto[it1, inserted1] = ClassBlackList.insert(typeid(Class3));
std::cout << "inserted: " << inserted1 << '\n';
auto[it2, inserted2] = ClassBlackList.insert(typeid(Class3));
std::cout << "inserted: " << inserted2 << '\n';
}
Output:
inserted: 1
inserted: 0
Is it possible to use lambda for hashing in hashed_<non>_unique interface for boost::multi_index?
See this example: https://godbolt.org/z/1voof3
I also saw this: How to use lambda function as hash function in unordered_map? where the answer says:
You need to pass lambda object to unordered_map constructor since lambda types are not default constructible.
and I'm not sure is it even possible to do for the given example on godbolt.
Starting with C++20, yes, you can: https://godbolt.org/z/fTbzPP (note f is declared as auto const hash_f, without a &).
As for #sehe's claim that multi_index_containers can't be passed instances of hash objects (or other intervening function objects) at construction time, the claim is incorrect: they can, although the interface is somewhat complicated:
Live Coliru Demo
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <functional>
struct non_default_ctble_hash
{
non_default_ctble_hash(std::size_t n):n{n}{}
template<typename T>
std::size_t operator()(const T& x){return std::hash<T>{}(x)*n;}
std::size_t n;
};
using namespace boost::multi_index;
using container=multi_index_container<
int,
indexed_by<
hashed_unique<identity<int>,non_default_ctble_hash>
>
>;
int main()
{
container::ctor_args_list cal{
{0,identity<int>{},non_default_ctble_hash{666},std::equal_to<int>{}}
};
container c(cal);
}
I don't think you can. With a standard container you would have had to supply the actual instance to the constructor. However, MultiIndex doesn't afford that:
docs
As explained in the index concepts section, indices do not have public constructors or destructors. Assignment, on the other hand, is provided. Upon construction, max_load_factor() is 1.0.
Loophole?
You can perhaps get away with a locally defined class:
auto const hash_f = [](int const& n) { return std::hash<int>()(n); };
struct HashType : decltype(hash_f) {};
using AnimalsMultiIndex = multi_index_container<
Animal, indexed_by<hashed_non_unique<
tag<animal_legs>, member<Animal, LegsType, &Animal::legs>,
HashType>>>;
AnimalsMultiIndex animals;
Which does work: c++20 required
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/tag.hpp>
#include <boost/multi_index_container.hpp>
#include <iostream>
#include <string>
using namespace boost::multi_index;
using LegsType = int;
struct Animal {
std::string name;
LegsType legs;
};
// tags
struct animal_legs {};
int main() {
// using lambda doesn't work for hashing
auto const hash_f = [](int const& n) { return std::hash<int>()(n); };
struct HashType : decltype(hash_f) {};
using AnimalsMultiIndex = multi_index_container<
Animal, indexed_by<hashed_non_unique<
tag<animal_legs>, member<Animal, LegsType, &Animal::legs>,
HashType>>>;
AnimalsMultiIndex animals;
animals.insert({ "cat", 4 });
auto const& legs_index = animals.get<animal_legs>();
int num_of_legs = 4;
std::cout << "Number of animals that have " << num_of_legs
<< " legs is: " << legs_index.count(num_of_legs) << '\n';
}
Prints
Number of animals that have 4 legs is: 1
I need to copy the contents of a std::list into an array, wherein the array is struct of array. Below is the code implementation of it.
#include <iostream>
#include <string>
using namespace std;
typedef struct
{
int height;
int width;
int length;
}dimensions;
GetDimensions(list<std::string>, *int); // Function that copies the content of list to array passed as second parameter
int main()
{
dimensions cuboid[10];
int plane[10];
list<std::string> planeList = GetList();//Function that returns list of elements
list<std::string> dimensionList = GetList();
GetDimensions(planeList,&plane);//This is fine, as it is a simple array
GetDimensions(dimensionList,&cuboid.height);//Trouble in implementation of this usecase, for cuboid.height, cuboid.width and cuboid.height.
return 0;
}
GetDimensions(list<std::string>dimensionList, int* dimensionParams)
{
int i=0;
for(list<std::string>::iterator it = dimensionList.begin(); it != dimensionList.end(); ++it)
{
dimensionParams[i] = stoi(*it);
i++;
}
}
Here, I need GetDimensions() function to copy the list (passed as first parameter) to array (second parameter). The implemented function works well for simple array plane. But how to pass the array of struct as parameter to the function ?
I will be getting the std::list as cuboid.height, cuboid.width and cuboid.length. So the function has to copy the contents of list from cuboid[0].height to cuboid[i].height respectively. Is there any specific function to copy the content directly?
Use std::array 's instead. Then your problem can be reduced to passing two different types of arrays to a single function.
This can be solved
either by good old function overloads
or in c++17 function template with
if-constexpr.
Following is an example code with templated function with if-constexpr (See live online)
#include <iostream>
#include <string>
#include <list>
#include <array>
#include <type_traits> // std::is_same_v
struct dimensions // no need to typedef here
{
int height;
int width;
int length;
};
template<typename T>
void GetDimensions(const list<std::string>& dimensionList, T& dimensionParams)
^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //---> pass list by const-ref as the values are non-modifying
{
int i{0};
if constexpr (std::is_same_v<std::array<int, 10>, T>)
{
for(const std::string& str: dimensionList) dimensionParams[i++] = std::stoi(str);
}
else
{
for(const std::string& str: dimensionList) dimensionParams[i++].height = std::stoi(str);
}
}
int main()
{
std::array<dimensions, 10> cuboid; // use std::array instead of VLA
std::array<int, 10> plane;
std::list<std::string> planeList{"1", "2"}; // some list
std::list<std::string> dimensionList{"1", "2"};
GetDimensions(planeList, plane);
GetDimensions(dimensionList, cuboid);
return 0;
}
Also note that:
You have not specified the return type of GetDimensions function.
You probably want to return void there.
in C++ you do not need to use typedef alias for struct { ... }.
last but not least, do not practice with using namespace std;
You can do this with boost::transform_iterator.
#include <iostream>
#include <string>
#include <algorithm>
#include <functional>
#include <boost/iterator/transform_iterator.hpp>
struct dimensions {
int height;
int width;
int length;
};
template <typename OutputIt>
void GetDimensions(std::list<std::string> dimensionList, OutputIt dimensionParams)
{
// N.b. taking the address of a standard library function is undefined, so wrap in a lambda
auto stoi = [](std::string s){ return std::stoi(s); };
std::copy(boost::make_transform_iterator(dimensionList.begin(), stoi),
boost::make_transform_iterator(dimensionList.end(), stoi),
dimensionParams);
}
int main() {
dimensions cuboid[10];
int plane[10];
std::list<std::string> planeList = GetList();
std::list<std::string> heightList = GetList();
std::list<std::string> widthList = GetList();
std::list<std::string> lengthList = GetList();
GetDimensions(planeList, plane);
GetDimensions(heightList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::height)));
GetDimensions(widthList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::width)));
GetDimensions(lengthList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::length)));
return 0;
}
Here is a snippet of code which causes a C2664 error:
cannot convert argument 1 from 'std::unique_ptr<Component,std::default_delete<_Ty>>' to 'ComPtr &'
So why must a non-const reference be initialized with an lvalue? How to avoid this except by declaring a new variable?
#include <memory>
#include <list>
class Component {
};
using ComPtr = unique_ptr<Component>;
using ComList = list<ComPtr>;
ComList coms;
void addComponent(ComPtr&c) {
coms.push_back(c);
}
int main() {
addComponent(make_unique<Component>()); //Error here.
return 0;
}
The way to write this so you don't have to do what you're fighting with is: https://godbolt.org/g/vceL4q
#include <memory>
#include <list>
using namespace std;
class Component {
};
using ComPtr = unique_ptr<Component>;
using ComList = list<ComPtr>;
ComList coms;
void addComponent(ComPtr c) { // <== change here
coms.push_back(std::move(c)); // and here
}
int main() {
addComponent(make_unique<Component>());
return 0;
}
The c in addComponent will be created via a move constructor because the result of make_unique is an rvalue.
It's preferred to pass in large (move friendly) data structures by value this way.
I have a class Foo with a member variable of type std::vector<std::unique_ptr<Bar>>, which I would like to fill in the initialization list of the constructor of this class. Is this possible?
I was hoping that using the fill constructor of vector would be possible, something like this
Foo::Foo(int n):
vector<unique_ptr<Bar>> (n, unique_ptr<Bar> (new Bar))
{}
but I think this requires copy constructor of std::unique_ptr, which is deleted (as it should be) (unique_ptr(const unique_ptr&) = delete).
Is there a better way to go about this?
Since it is not copyable, move it!
Solution with hard-coded objects:
#include <memory>
#include <vector>
#include <iterator>
class Bar{};
class Foo{
public:
Foo():bars(get_bars()) {}
std::vector<std::unique_ptr<Bar>> bars;
private:
std::vector<std::unique_ptr<Bar>> get_bars(){
std::unique_ptr<Bar> inilizer_list_temp[]={std::make_unique<Bar>(),std::make_unique<Bar>(),std::make_unique<Bar>()};
return std::vector<std::unique_ptr<Bar>>{std::make_move_iterator(std::begin(inilizer_list_temp)),std::make_move_iterator(std::end(inilizer_list_temp))};
}
};
int main()
{
Foo foo;
}
Live Demo
Solution with Dynamic number of objects:
#include <memory>
#include <vector>
#include <iterator>
#include <iostream>
class Bar{
public:
int a=5;
};
class Foo{
public:
Foo():bars(get_bars(10)) {}
std::vector<std::unique_ptr<Bar>> bars;
private:
std::vector<std::unique_ptr<Bar>> get_bars(int n){
std::vector<std::unique_ptr<Bar>> inilizer_list_temp;
inilizer_list_temp.reserve(n);
for(size_t i=0;i<n;++i){
inilizer_list_temp.emplace_back(std::make_unique<Bar>());
}
return inilizer_list_temp;
}
};
int main()
{
Foo foo;
for(auto const& item:foo.bars){
std::cout << item->a;
}
}
Live Demo
And see this for more details Can I list-initialize a vector of move-only type?
EDIT:
For C++11 users with no std::make_uniuqe:
template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args )
{
return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}
Source