Reputation: 30028
I tried to specify just the constructor as the last argument of transform but it did not work since in C++ you can not take address of a constructor, but also signature would not match, constructor does not have return value.
I can easily wrap it in a lambda, but I wonder is there a simpler way to do this?
constructor functor does not look too bad, but it is not in std::
, I would prefer some standard way of doing this(I looked into std::allocator<T>::construct
, but signature does not match).
Here are my attempts:
#include <iostream>
#include <utility>
#include <iterator>
#include <algorithm>
#include <vector>
struct Square{
explicit Square(const int a):a(a){}
int a;
};
template<typename T>
struct constructor{
template<typename... Args>
T operator()(Args&&... args) {
return T(std::forward<Args>(args)...);
}
};
int main() {
std::vector<int> ints{1,2};
std::vector<Square> squares;
// :( can not take address of a constructor
//std::transform(ints.begin(), ints.end(), std::back_inserter(squares), &Square::Square);
// :/ works, but requires manual implementation of constructor
std::transform(ints.begin(), ints.end(), std::back_inserter(squares), constructor<Square>{});
// :/ works, but spammy
// std::transform(ints.begin(), ints.end(), std::back_inserter(squares), [](const int i) {return Square(i);});
for (const auto square:squares){
std::cout << square.a << std::endl;
}
}
Upvotes: 5
Views: 1309
Reputation: 6731
Limiting ourselves to standard C++20 facilities, I would suggest simply using the vector constructor taking iterators (which might require a tweak in the constructor of Square
as stated below):
#include <iostream>
#include <utility>
#include <iterator>
#include <algorithm>
#include <vector>
struct Square{
explicit Square(const int a):a(a){}
int a;
};
int main() {
std::vector<int> ints{1,2};
std::vector<Square> squares(ints.begin(), ints.end());
for (const auto square:squares){
std::cout << square.a << std::endl;
}
}
This is accepted by GCC/libstdc++ but I’m not 100% sure it should because the standard seems to imply that the elements referred to by the iterators must implicitly convertible to the vector
value_type
(see this answer for more details), and your constructor is explicit
.
Future revisions of the standard are expected to gain ranges::to
. The current proposal P1206R2 has an example suggesting that the types of the input range elements must be ConvertibleTo
the type of the elements in the container, and that concept isn’t satisfied if you only have an explicit
constructor.
In fact, the range-v3 implementation of ranges::to
requires the constructor to be a converting constructor for your example to work:
#include <iostream>
#include <utility>
#include <iterator>
#include <algorithm>
#include <vector>
#include <range/v3/all.hpp>
struct Square{
/*explicit*/ Square(const int a):a(a){}
int a;
};
int main() {
std::vector<int> ints{1,2};
std::vector<Square> squares = ints | ranges::to<std::vector<Square>>();
for (const auto square:squares){
std::cout << square.a << std::endl;
}
}
Upvotes: 3