I have a piece of code to use the RTTI as case selector
#include <iostream>
#include <vector>
#include <typeinfo>
#include <complex>
#include <algorithm>
using namespace std;
typedef complex<int> data;
typedef std::vector< data > List;
template <typename T> void zeros(T &v)
{
if (typeid(typename T::value_type)==typeid(complex<int>))
std::fill(v.begin(), v.end(), complex<int>(0, 0));
else std::fill(v.begin(), v.end(), static_cast<typename T::value_type>(0));
}
int main(int argc, char *argv[])
{
List A;
zeros(A);
return 0;
}
In above code, I use typename T::value_type to determine the data type of the list, if there is complex then I will fill the list with complex zero. Otherwise, make it zero of the corresponding type. In above code, if I set data to be int, it does compile and run without any problem. But if I set it another other type, it fail to compile. I wonder if there is any way to make above code work for different type of data (for float, double, long, int, complex, complex and complex).
p.s. the reason I want to fill the vector in two cases because I am going to create my own complex for some reason and I have to tell the code how to create the complex zero or real zero. But for testing, I can't even make it work with built-in datatype
It seems that you want both a default initialized complex (0, 0), and a zero initialized int. You can achieve this using value initialization:
template <typename T> void zeros(T &v)
{
std::fill(v.begin(), v.end(), typename T::value_type{});
}
If you really want to perform some branching depending on what is T::value_type, then it would be better to use some type trait, like the following one:
template<typename T>
struct default_value {
static const T value;
};
template<>
struct default_value<std::complex<int>> { // assuming you'll use other complex type
static const std::complex<int> value;
};
template<typename T>
const T default_value<T>::value = T{};
const std::complex<int> default_value<std::complex<int>>::value = std::complex<int>(0, 0);
// now use it
template <typename T> void zeros(T &v)
{
std::fill(v.begin(), v.end(), default_value<typename T::value_type>::value);
}
But again, default construction and value initialization for built in types should be enough.
Related
I'm thinking about a function with signature
template<typename ...Ts>
std::vector<std::tuple<Ts...>> join_vectors(std::vector<Ts>&&...) {
//...
};
but probably a more general one accepting any iterable instead of just std::vector would be good. Probably it would have a signature like this?
template<template<typename> typename C, typename ...Ts>
C<std::tuple<Ts...>> join_vectors(C<Ts>&&...) {
// ...
};
However, I'm not at this level yet in C++ (despite doing the same in Haskell would be relatively easy), hence I seek for help.
Unfortunately, Range-v3's zip is not at my disposal in this case. I'm tagging it because I think those interested in it are in a better position to help me.
For any indexable containers with size something like this is possible:
#include <tuple>
#include <vector>
#include <algorithm>
// Copy from lvalue containers, move from rvalue containers.
template<typename ...Cs>
auto zip(Cs... vecs) {
std::vector<std::tuple<typename std::decay_t<Cs>::value_type...>> vec;
auto len = std::min({vecs.size()...});
vec.reserve(len);
for(std::size_t i=0;i<len;++i){
vec.emplace_back(std::move(vecs[i])...);
}
return vec;
};
//Return vector of tuples with & for non-const vecs and const& if const.
template<typename ...Cs>
auto zip_view(Cs&... vecs) {
std::vector<std::tuple<decltype(vecs[0])...>> vec;
auto len = std::min({vecs.size()...});
vec.reserve(len);
for(std::size_t i=0;i<len;++i){
vec.emplace_back(vecs[i]...);
}
return vec;
};
If the containers have properly implemented move constructors, this solution will copy the containers passed as lvalues and move from rvalue ones.
Very slight downside is that lvalue containers are copied whole first instead of only the individual elements.
Example [Godbolt]
#include <iostream>
#include <memory>
template<typename T, typename...Args>
void print_tuple(const T& first, const Args&... args){
std::cout<<'('<<first;
((std::cout<<','<< args),...);
std::cout<<')';
}
template<typename T>
struct helper{
using fnc_t = void;
};
template<typename...Args>
struct helper<std::tuple<Args...>>{
using fnc_t = void(*)(const Args&... args);
};
template<typename...Args>
struct helper<std::tuple<Args&...>>{
using fnc_t = void(*)(const Args&... args);
};
template<typename T>
using fnc_t2 = typename helper<T>::fnc_t;
template<typename T>
void template_apply(fnc_t2<T> f, const T& tuple){
std::apply(f, tuple);
}
template<typename T>
void print_vec(const std::vector<T>& vec){
for(const auto&e:vec){
template_apply(print_tuple,e);
std::cout<<'\n';
}
}
struct MoveOnlyFoo{
MoveOnlyFoo(int i):m_i(i){}
int m_i;
std::unique_ptr<int> ptr = nullptr;
};
std::ostream& operator<<(std::ostream& o, const MoveOnlyFoo& foo){
return o<<foo.m_i;
}
int main(){
std::vector v1{1,2,3,4,5,6};
std::vector v2{'a','b','c','d','e'};
std::vector v3{1.5,3.5,7.5};
std::vector<MoveOnlyFoo> vmove;
vmove.emplace_back(45);
vmove.emplace_back(46);
vmove.emplace_back(47);
const std::vector v4{-1,-2,-3,-4,-5};
//Move rvalues, copy lvalue.
print_vec(zip(v1,v2,v3, v4, std::move(vmove)));
// This won't work since the elements from the last vector cannot be copied.
//print_vec(zip(v1,v2,v3, v4, vmove));
std::cout<<"View:\n";
//View, provides const& for const inputs, & for non-const
print_vec(zip_view(v1,v2,v3,v4));
std::cout<<"Modify and print:\n";
for(auto& [x,y]: zip_view(v1,v2)){
++x,++y;
}
// Note the view can work with const containers, returns tuple of `const T&`.
print_vec(zip_view(std::as_const(v1),std::as_const(v2)));
}
Output
(1,a,1.5,-1,45)
(2,b,3.5,-2,46)
(3,c,7.5,-3,47)
View:
(1,a,1.5,-1)
(2,b,3.5,-2)
(3,c,7.5,-3)
Modify and print:
(2,b)
(3,c)
(4,d)
(5,e)
(6,f)
Please disregard the readability of the printing code ;)
I modeled it after python zip functionality. Note your initial proposal copies the vectors, so the output is a vector with the values moved from the parameters.
Returning an iterable Cs is harder because you would have to specify how to insert elements into it, iterators cannot do it on their own.
Getting it work with iterators (but returning still a vector) is a chore, but in theory also possible.
I'm trying to study the concept functionality and syntax, but I failed to come up with a working example of std::vector, any idea of what is going wrong ?
https://gcc.godbolt.org/z/FHaQ-3
#include <vector>
#include <string>
template<typename T>
concept Compare = requires(T a, T b) {
{ a <=> b } -> std::same_as<std::partial_ordering>;
};
struct Cat {
int age;
std::string name;
auto operator<=>(const Cat&) const = default;
};
int main(int argc, char** argv) {
/* Single type: Fail
std::vector<Compare auto> vec{
Cat{4, "Faisca"},
Cat{4, "Neka"}
};
*/
/* Heterogenous: Fail
std::vector<Compare auto> vec{
Cat{4, "Faisca"}, 1, std::string{"Deu Ruim"}
};
*/
return 0;
}
You can't have a std::vector of heterogenous types.
However, you can have a std::vector of type T that satisfies a concept. Simply provide a alias template that is constrained:
template<typename T>
requires Compare<T>
using vec = std::vector<T>;
or with terse syntax:
template<Compare T>
using vec = std::vector<T>;
Now, you can do:
vec<Cat> v;
but not
vec<int> v;
Note that your Cat class as currently written, doesn't actually satisfy the concept Compare. If you change the return type of operator<=>, like this:
std::partial_ordering operator<=>(const Cat&) const = default;
you can create a vector of Cats:
vec<Cat> a{
Cat{4, "Faisca"},
Cat{4, "Neka"}
};
You cannot use placeholder types (auto and decltype(auto), with or without constraints) within template argument lists like that. You must either use a concrete type or rely on class template argument deduction from C++17 (by not specifying template arguments at all).
A vector<T> is a sequence container, and sequence containers contain objects of a given type. A single given type.
What's the use of value_type in STL containers?
From the MSDN:
// vector_value_type.cpp
// compile with: /EHsc
#include <vector>
#include <iostream>
int main( )
{
using namespace std;
vector<int>::value_type AnInt;
AnInt = 44;
cout << AnInt << endl;
}
I don't understand what does value_type achieve here?
The variable could be an int as well? Is it used because the coders are lazy to check what's the type of objects present in the vector?
I think these are also similar to it allocator_type,size_type,difference_type,reference,key_type etc..
Yes, in your example, it is pretty easy to know that you need an int. Where it gets complicated is generic programming. For example, if I wanted to write a generic sum() function, I would need it to know what kind of container to iterate and what type its elements are, so I would need to have something like this:
template<typename Container>
typename Container::value_type sum(const Container& cont)
{
typename Container::value_type total = 0;
for (const auto& e : cont)
total += e;
return total;
}
The following codes come from an example code to illustrate how to use boost::type_traits. It will use two methods to swap two variables. It is easy to understand that when the two variables are integer (int) their type traits correspond to true_type. However, when two variables are bool type they are not regarded as true_type any more. Why would it happen? Thanks.
#include <iostream>
#include <typeinfo>
#include <algorithm>
#include <iterator>
#include <vector>
#include <memory>
#include <boost/test/included/prg_exec_monitor.hpp>
#include <boost/type_traits.hpp>
using std::cout;
using std::endl;
using std::cin;
namespace opt{
//
// iter_swap:
// tests whether iterator is a proxying iterator or not, and
// uses optimal form accordingly:
//
namespace detail{
template <typename I>
static void do_swap(I one, I two, const boost::false_type&)
{
typedef typename std::iterator_traits<I>::value_type v_t;
v_t v = *one;
*one = *two;
*two = v;
}
template <typename I>
static void do_swap(I one, I two, const boost::true_type&)
{
using std::swap;
swap(*one, *two);
}
}
template <typename I1, typename I2>
inline void iter_swap(I1 one, I2 two)
{
//
// See is both arguments are non-proxying iterators,
// and if both iterator the same type:
//
typedef typename std::iterator_traits<I1>::reference r1_t;
typedef typename std::iterator_traits<I2>::reference r2_t;
typedef boost::integral_constant<bool,
::boost::is_reference<r1_t>::value
&& ::boost::is_reference<r2_t>::value
&& ::boost::is_same<r1_t, r2_t>::value> truth_type;
detail::do_swap(one, two, truth_type());
}
}; // namespace opt
int cpp_main(int argc, char* argv[])
{
//
// testing iter_swap
// really just a check that it does in fact compile...
std::vector<int> v1;
v1.push_back(0);
v1.push_back(1);
std::vector<bool> v2;
v2.push_back(0);
v2.push_back(1);
opt::iter_swap(v1.begin(), v1.begin()+1);
opt::iter_swap(v2.begin(), v2.begin()+1);
return 0;
}
You've got the answer there in your code (as a comment):
See is both arguments are non-proxying iterators
vector<bool> has proxy iterators, because you can't refer directly to a bit.
If vector<bool> stored its' elements as individual booleans (taking 1-4 bytes/entry, depending on the system), the iterators could be non-proxying. But instead, vector<bool> stores 8 entries/byte and uses proxy iterators.
I'm currently working on implementing some mathematical base operations and try to avoid using third party libraries as much as possible. I'm stuck at overloading the operator* for the multiplication of a Scalar*Vector and Vector*Scalar. The current code for the dot product of Scalar*Vector:
#include <vector>
#include <type_traits>
template<class Vector, class Scalar>
typename std::enable_if<std::is_floating_point<Scalar>::value, Vector>::type operator*
(
const Scalar &a,
const Vector &b
)
{
return Vector
(
a*b[0],
a*b[1],
a*b[2]
);
}
int main()
{
const std::vector<double> v1({1,2,3});
const double s1(2);
const auto result(s1*v1);
std::cout<< result << std::endl;
}
The compiler error message is:
error: invalid operands to binary expression ('double' and 'const std::vector')
Any advises on how to overload the * operator, so that both dot-products are possible? I do not intend to implement these two operators in a custom vector class, as overloaded operators. Rather than that, I aim for the templated operator.
I've found a great explanation here and used it to adjust it for the vector operations. In the vector class header file, you basically define via the struct is_vector that anything of type T is not a vector by default. In the following a all types that can act as a vector have to be listed explicitely, such as std::vector.
#include <vector>
#include <type_traits>
template <typename T>
struct is_vector
{
static const bool value = false;
};
template <>
struct is_vector< std::vector >
{
static const bool value = true;
};
template <class Vector>
typename std::enable_if<std::is_vector<Vector>::value, double>::type
operator*(const Vector &a, const Vector &b);
And in the executable, the code looks the same.
int main()
{
const std::vector<double> v1({1,2,3});
const double s1(2);
const auto result(s1*v1);
std::cout<< result << std::endl;
}