struct stream_type1 {
template<typename T>
const T& read() const;
};
struct stream_type2 {
template<typename T>
const T& read() const;
};
template<typename S, typename T>
const T& stream_read(const S& stream)
{
return stream.read<T>();
}
// example:
stream_type1 stream1;
stream_type1 stream2;
int value1 = stream_read<int>(stream1);
int value2 = stream_read<int>(stream2);
error: C2665: 'stream_read': none of the 2 overloads could convert all the argument types
so, I have to specialize the template witch makes it redundant
template<typename T>
const T& stream_read(const stream_type1 & stream)
{
return stream.read<T>();
}
template<typename T>
const T& stream_read(const stream_type2 & stream)
{
return stream.read<T>();
}
You have your template parameters the wrong way round to deduce the stream type. At the moment you have instantiated it as
template<typename T>
const T& stream_read(const int& stream)
{
return stream.read<T>();
}
You can swap T and S
template<typename T, typename S>
const T& stream_read(const S& stream)
{
return stream.read<T>();
}
Related
Long story for this question at the end.
What is the easiest way to create a function behaving like std::forward but returning an lref for everything that is an lref and a const & ref in all other cases? E.g. with the following "conversion" table:
arg
myforward(arg)
T&
T&
T&&
const T&
const T&
const T&
const T&&
const T&
My main question is why the obvious method is not working for me (having four function accepting T&, T&&, const T&, const T&& with only the first returning a T&. Code I'm currently using (note, helper for monitoring creation and deletion of objects and printing types at the end):
#include <iostream>
using namespace std;
// Implementation #1
template <typename T> T & flr1(T & value) {/* std::cout<<" picked flr1 &"<<endl;*/ return value;};
template <typename T> const T &flr1(T && value) {/* std::cout<<" picked flr1 &&"<<endl; */return static_cast<const T&>(value);};
template <typename T> const T & flr1(const T & value) {/* std::cout<<" picked flr1 const &"<<endl; */return static_cast<const T&>(value);};
template <typename T> const T & flr1(const T && value) {/* std::cout<<" picked flr1 const &&"<<endl; */return static_cast<const T&>(value);};
// Implementation #2 (same results as #1)
//template<typename T> using myremove_cref_t = typename remove_const<typename remove_reference<T>::type>::type;
//template <typename T> myremove_cref_t<T> & flr2(myremove_cref_t<T> & value) { return value;};
//template <typename T> const myremove_cref_t<T> &flr2(myremove_cref_t<T> && value) { return value;};
//template <typename T> const myremove_cref_t<T> & flr2(const myremove_cref_t<T> & value) { return value;};
//template <typename T> const myremove_cref_t<T> & flr2(const myremove_cref_t<T> && value) { return value;};
// Implementation #3
template <typename T> struct forwardLRefOrConst { typedef T &type; static const char *name() {return "generic";}};
template <typename T> struct forwardLRefOrConst<T&> { typedef T &type; static const char *name() {return "T&";}};
template <typename T> struct forwardLRefOrConst<T &&> { typedef const T &type; static const char *name() {return "T &&";}};
template <typename T> struct forwardLRefOrConst<const T &> { typedef const T &type; static const char *name() {return "const T&";}};
template <typename T> using forwardLRefOrConst_t = typename forwardLRefOrConst<T>::type;
template <typename T> forwardLRefOrConst_t<T> flr3(T &value) { return static_cast<forwardLRefOrConst_t<T>>(value); }
// Test code
template <typename T> void test2(const char *name, T && arg)
{
cout<<" test "<<name<<" is "<<Name<decltype(arg)>::name()<<endl;
}
template <typename T> void test(T &&arg)
{
cout<<" test arg is "<<Name<decltype(arg)>::name()<<endl;
test2("flr1(arg)",flr1<T>(arg)); // Implementation 1
test2("flr1(std::forward(arg))",flr1<T>(std::forward<T>(arg))); // Implementation 1 bis
test2("flr3(arg)",flr3<decltype(arg)>(arg)); // Implementation 3
}
int main()
{
cout<<"Initialization"<<endl;
ClassA a1;
const ClassA a2;
ClassA &a3(a1);
ClassA &&a4=ClassA();
const ClassA &&a5=ClassA();
cout<<("Test (T)")<<endl;
test(a1);
cout<<("Test (const T)")<<endl;
test(a2);
cout<<("Test (T &)")<<endl;
test(a3);
cout<<("Test (T &&)")<<endl;
test(std::forward<ClassA>(a4));
cout<<("Test (immediate)")<<endl;
test(ClassA());
cout<<("Test (const T&&)")<<endl;
test(a5);
cout<<"End"<<endl;
return 0;
}
This program generates the following output
Initialization
Constructed A1
Constructed A2
Constructed A3
Constructed A4
Test (T)
test arg is &T
test flr1(arg) is &T
test flr1(std::forward(arg)) is &T
test flr3(arg) is &T
Test (const T)
test arg is const &T
test flr1(arg) is const &T
test flr1(std::forward(arg)) is const &T
test flr3(arg) is const &T
Test (T &)
test arg is &T
test flr1(arg) is &T
test flr1(std::forward(arg)) is &T
test flr3(arg) is &T
Test (T &&)
test arg is &&T
test flr1(arg) is &T
test flr1(std::forward(arg)) is const &T
test flr3(arg) is const &T
Test (immediate)
Constructed A5
test arg is &&T
test flr1(arg) is &T
test flr1(std::forward(arg)) is const &T
test flr3(arg) is const &T
Destroyed A5
Test (const T&&)
test arg is const &T
test flr1(arg) is const &T
test flr1(std::forward(arg)) is const &T
test flr3(arg) is const &T
End
Destroyed A4
Destroyed A3
Destroyed A2
Destroyed A1
As visible above only flr1<T>(std::forward<T>(arg)) or flr3<decltype(arg)>(arg) give the expected result but they are both too verbose for my taste. I would like to have a simple function myforward(arg) or at worst myfoward<T>(arg). Is there any way to achieve that?
/* Test class for arguments */
static int index=1;
template <int I> struct TestClass
{
TestClass(int val): val(val)
{
cout<<" Constructed "<<id()<<val<<endl;
if(index<=val)
index=val+1;
}
TestClass(): val(index++)
{
cout<<" Constructed "<<id()<<val<<endl;
}
TestClass(const TestClass<I> &value): val(index++)
{
cout<<" Constructed from "<<id()<<value.val<<" to "<<id()<<val<<endl;
}
TestClass(TestClass<I> &&value): val(index++)
{
cout<<" Moved "<<id()<<value.val<<" to "<<id()<<val<<endl;
val=value.val;
value.val=0;
}
void operator =(TestClass &&value)
{
cout<<" Assign moved "<<id()<<value.val<<" to "<<id()<<val<<endl;
val=value.val;
value.val=0;
}
void operator =(const TestClass &value)
{
cout<<" Assign copied "<<id()<<value.val<<" to "<<id()<<val<<endl;
}
~TestClass()
{
if(val)
cout<<" Destroyed "<<id()<<val<<endl;
}
char id() const {return 'A'+I;}
int val;
};
typedef TestClass<0> ClassA;
typedef TestClass<1> ClassB;
/* Functions to print a data type */
template <typename T> struct Name
{
static std::string name()
{
std::string ret;
if(std::is_const<T>())
ret="const ";
if(std::is_lvalue_reference<T>())
ret+=Name<typename std::remove_reference<T>::type>::name();
else if(std::is_rvalue_reference<T>())
ret+=Name<typename std::remove_reference<T>::type>::name();
else
ret+="T";
return ret;
}
};
template <typename T> struct Name<T &>
{
static std::string name()
{
std::string ret;
if(std::is_const<T>())
ret="const ";
ret+="&T";
return ret;
}
};
template <typename T> struct Name<T &&>
{
static std::string name()
{
std::string ret;
if(std::is_const<T>())
ret="const ";
ret+="&&T";
return ret;
}
};
I'm currently creating a small C++/Qt wrapper for Sqlite. I've written in the Query all the overloads for binding:
bool bind(int col, int value);
bool bind(int col, double value);
and the ones for fetching:
bool columnSingle(int col, int &value);
bool columnSingle(int col, double &value);
Together with its parameter pack variants:
template <typename ...T> bool bindMany(int startCol, const T &...values);
template <typename ...T> bool fetchMany(int startCol, T &...values);
and its version working with tuples.
Now I'm currently working on a function that performs in a single operation the bind of N parameters, a single step and the fetch of M columns:
template <int N, typename ...T> bool querySingle(T &&...values);
In its body I transform values to a tuple (Let's call it A) and then create from it two tuples:
The first containing the first N values and the other the remaining ones.
The problem is that if I create it with std::forward_as_tuple the fetching part will work even if I pass a immediate instead of throwing an error as expected.
e.g.
querySingle<2>(4, "Foo", 5 /* This should give an error, all parameters after the 2nd should be an lref */ , a);
I was thus trying to create a forward function behaving as described above.
I would simply do
template <typename T>
decltype(auto) myforward(T&& value)
{
if constexpr (std::is_rvalue_reference_v<T&&>
or std::is_const_v<std::remove_reference_t<T>>) {
return static_cast<const T&>(value);
} else {
return static_cast<T&>(value);
}
}
Demo
As visible above only flr1<T>(std::forward<T>(arg)) or flr3<decltype(arg)>(arg) give the expected result
Notice that inside the function:
template <typename T> void test(T&& arg)
arg has name and is so a l-value. To keep/retrieve its original form, you have either to std::forward<T>(arg) or provide T to your template and work accordingly:
template <typename T, typename U>
decltype(auto) myforward2(U&& value)
{
if constexpr (std::is_rvalue_reference_v<T&&>
or std::is_const_v<std::remove_reference_t<T>>) {
return static_cast<const T&>(value);
} else {
return static_cast<T&>(value);
}
}
template <typename T>
decltype(auto) test(T&& arg)
{
return myforward2<T>(arg); // arg is l-value here
}
Demo
In real applications I should stick to the standard library facilities, for practice and understanding how those facilities work I should try to implement my own.
Here I've implemented a simulation of the smart pointer unique_ptr:
#include<iostream>
#include <memory>
template <typename T>
class DefDel
{
public:
template <typename U>
void operator()(U* p)const
{
std::cout << "freeing memory...\n";
delete p;
}
};
template <typename T>
class DefDel<T[]>
{
public:
template <typename U>
void operator()(U* p)const
{
std::cout << "freeing memory of an array of objects...\n";
delete[] p;
}
};
template <typename T, typename D = DefDel<T>>
class UniqPtr final
{
public:
UniqPtr(T* = nullptr, D = DefDel<T>{});
UniqPtr(UniqPtr const&) = delete;
UniqPtr(UniqPtr&&) noexcept;
UniqPtr& operator =(UniqPtr const&) = delete;
UniqPtr& operator =(UniqPtr&&) noexcept;
~UniqPtr();
T& operator*();
T const& operator*() const;
T* operator->();
T const* operator->() const;
operator bool() const;
private:
T* ptr_{nullptr};
D del_{};
};
template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(T* p, D del) :
ptr_(p),
del_(del)
{}
template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(UniqPtr&& rhs) noexcept :
ptr_(std::move(rhs.ptr_)),
del_(std::move(rhs.del_))
{
rhs.ptr_ = nullptr;
}
template <typename T, typename D>
UniqPtr<T, D>& UniqPtr<T, D>::operator = (UniqPtr&& rhs) noexcept
{
if(this != &rhs)
{
ptr_ = std::move(rhs.ptr_);
del_ = std::move(rhs.del_);
rhs.ptr_ = nullptr;
}
return *this;
}
template <typename T, typename D>
UniqPtr<T, D>::~UniqPtr()
{
del_(ptr_);
}
template <typename T, typename D>
T& UniqPtr<T, D>::operator*()
{
return *ptr_;
}
template <typename T, typename D>
T const& UniqPtr<T, D>::operator*() const
{
return *ptr_;
}
template <typename T, typename D>
T* UniqPtr<T, D>::operator->()
{
return ptr_;
}
template <typename T, typename D>
T const* UniqPtr<T, D>::operator->() const
{
return ptr_;
}
template <typename T, typename D>
UniqPtr<T, D>::operator bool() const
{
return ptr_;
}
// for array
template <typename T, typename D>
class UniqPtr<T[], D> final
{
public:
UniqPtr(T* = nullptr, D = DefDel<T[]>{});
UniqPtr(UniqPtr const&) = delete;
UniqPtr(UniqPtr&&) noexcept;
UniqPtr& operator =(UniqPtr const&) = delete;
UniqPtr& operator =(UniqPtr&&) noexcept;
~UniqPtr();
T& operator*();
T const& operator*() const;
T* operator->();
T const* operator->() const;
operator bool() const;
private:
T* ptr_{nullptr};
D del_{};
};
template <typename T, typename D>
UniqPtr<T[], D>::UniqPtr(T* p, D del) :
ptr_(p),
del_(del)
{}
template <typename T, typename D>
UniqPtr<T[], D>::UniqPtr(UniqPtr&& rhs) noexcept :
ptr_(std::move(rhs.ptr_)),
del_(std::move(rhs.del_))
{
rhs.ptr_ = nullptr;
}
template <typename T, typename D>
UniqPtr<T[], D>& UniqPtr<T[], D>::operator = (UniqPtr&& rhs) noexcept
{
if(this != &rhs)
{
ptr_ = std::move(rhs.ptr_);
del_ = std::move(rhs.del_);
rhs.ptr_ = nullptr;
}
return *this;
}
template <typename T, typename D>
UniqPtr<T[], D>::~UniqPtr()
{
del_(ptr_);
}
template <typename T, typename D>
T& UniqPtr<T[], D>::operator*()
{
return *ptr_;
}
template <typename T, typename D>
T const& UniqPtr<T[], D>::operator*() const
{
return *ptr_;
}
template <typename T, typename D>
T* UniqPtr<T[], D>::operator->()
{
return ptr_;
}
template <typename T, typename D>
T const* UniqPtr<T[], D>::operator->() const
{
return ptr_;
}
template <typename T, typename D>
UniqPtr<T[], D>::operator bool() const
{
return ptr_;
}
int main()
{
UniqPtr<int[]> upi(new int[3]{57});
std::cout << sizeof(upi) << '\n';
std::unique_ptr<int[], DefDel<int[]>> upi2(new int[3]{57});
std::cout << sizeof(upi2) << '\n';
}
Why the size of my UniqPtr objects are double in size as std::unique_ptr (even being initialized with the same values)?
Is that because of my class is storing a Del_ object as a member?
If that is the problem then how could I achieve the very similar behavior as unique_ptr with 0 cost?
Why the size of my UniqPtr objects are double in size as std::unique_ptr (even being initialized with the same values)?
Is that because of my class is storing a Del_ object as a member?
Yes.
Because D del_ needs to have storage, and every T* ptr_ has to be properly aligned
If that is the problem then how could I achieve the very similar behavior as unique_ptr with 0 cost?
You can privately derive from D, rather than have it be a member. Then an instantiation with an empty class may cave it occupy no extra storage.
template <typename T, typename D = DefDel<T>>
class UniqPtr final : D
{
public:
UniqPtr(T* = nullptr, D = {});
UniqPtr(UniqPtr const&) = delete;
UniqPtr(UniqPtr&&) noexcept;
UniqPtr& operator =(UniqPtr const&) = delete;
UniqPtr& operator =(UniqPtr&&) noexcept;
~UniqPtr();
T& operator*();
T const& operator*() const;
T* operator->();
T const* operator->() const;
operator bool() const;
private:
T* ptr_{nullptr};
};
template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(T* p, D del) :
D(del),
ptr_(p)
{}
template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(UniqPtr&& rhs) noexcept :
D(std::move(*rhs)),
ptr_(std::exchange(rhs.ptr_, nullptr))
{}
template <typename T, typename D>
UniqPtr<T, D>& UniqPtr<T, D>::operator = (UniqPtr&& rhs) noexcept
{
using std::swap;
swap(static_cast<D&>(*this), static_cast<D&>(rhs));
swap(ptr_, rhs.ptr_);
}
template <typename T, typename D>
UniqPtr<T, D>::~UniqPtr()
{
static_cast<D&>(*this)(ptr_);
}
I have a template ReadVector function that reads std::vector from a stream:
template <class Stream, typename T>
inline void ReadVector(Stream & s, std::vector<T> & items)
{
s.Read(reinterpret_cast<uint8_t *>(items.data()), items.size() * sizeof(T));
}
with the specialization for boolean vector:
template <class Stream>
void ReadVector(Stream & s, std::vector<bool> & x)
{
...
}
the code above compiles, but I'd like to make the first function called only if T is an arithmetic type, so the condition should be like this:
std::enable_if<std::is_arithmetic<T>::value && !std::is_same(T, bool)::value
but I cannot figure out what is the syntax.
Also I'd like to have yet another specialization of ReadVector if T is not bool and is not arithmetic.
My first idea was something like this:
template <class Stream, typename T>
void ReadVector(Stream & s, std::vector<T> & items);
template <class Stream, typename T>
typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, void>::type ReadVector(Stream & s, std::vector<T> & items)
{
...
}
but this results in ambiguous call to overloaded function.
Figured this out!
template <class Stream, typename T>
typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, void>::type ReadVector(Stream & s, std::vector<T> & v)
{
s.Read(reinterpret_cast<uint8_t *>(v.data()), v.size() * sizeof(T));
}
template <class Stream, typename T>
typename std::enable_if<std::is_class<T>::value, void>::type ReadVector(Stream & s, std::vector<T> & v)
{
for (auto & elem : v)
{
Read(s, elem);
}
}
template <class Stream>
void ReadVector(Stream & s, std::vector<bool> & x)
{
...
}
I think your approach is wrong!
Why not just use the vector constructor?
std::vector<int> v(std::istream_iterator<int>(stream), std::istream_iterator<int>());
Don't need a function when it can be done in the vector initializer.
If you want to do something fancy (like read raw data from the stream) you just define a class for reading that specific type of data.
struct RawDataToInt
{
int value;
operator int() {return value;}
friend std::istream& operator>>(std::istream& str, RawDataToInt& v)
{
return str.read(reinterpret_cast<char*>(&v.value), sizeof(int));
}
};
....
std::vector<int> v(std::istream_iterator<RawDataToInt>(stream), std::istream_iterator<RawDataToInt>());
I have a mechanism that uses function overloads to decompose a type into primitives. Here is a sample of the mechanism:
template <typename M, typename T> void process(M&, T&);
template <typename M, typename T>
void process_impl(M& m, const T& t) {
m.write(t);
}
template <typename M, typename T>
void process_impl(M& m, const std::vector<T>& v) {
for (const auto& t: v) {
process(m, t);
}
}
template <typename M, typename T>
void process(M& m, const T& t) {
process_impl(m, t);
}
The use case is to feed a type to process(), which will decompose it into simple types using the overloads of process_impl(). In order to customize the behavior of process() for a specific type, simply overload process():
struct media {
template <typename T> void write(const T& t) { std::cout << t << std::endl; }
};
void process(media& m, const std::string& s) {
process(m, s.size());
}
This code:
media m;
process(m, std::vector<std::string>{"1", "22"});
Outputs:
1
2
Now, I want to wrap media in a class, such that I can further tweak the behavior of process() for my wrapper structure, but I want to preserve the initial behavior of process() regarding media:
template <typename M>
struct debug_media {
template <typename T> void write(const T& t) { m.write(t); }
M m;
};
template <typename M, typename T>
void process(debug_media<M>& d, const T& t) {
std::cout << "debug: ";
process(d.m, t);
}
Ideally, I want the output to be:
debug: 1
debug: 2
But that is not the case, since the overload for debug_media is used only once, for the first invocation; afterward, the overloads of process() that accept a media are used, not the one with debug_media<media>.
I cannot have inheritance between media and debug_media. Is it possible to preserve the behavior of both overloads?
I have put sample code on ideone.
You can add the extra overload:
template <typename M, typename T> void process(debug_media<M>& d, const std::vector<T>& v)
{
for(const auto& t : v) {
process(d, t);
}
}
Live example
I'm trying to specialize a function for a range of types using std::enable_if.
Here is a a simpler version of what I'm trying to accomplish.
#include <type_traits>
#include <string>
struct Ip
{};
template <typename T>
bool Equal(const std::string& s, const T& data)
{return s == data;}
template <typename T>
bool Equal(const std::string& s, const typename std::enable_if<std::is_integral<T>::value, T>::type& data)
{
//int specific
}
template <typename T>
bool Equal(const std::string& s, const typename std::enable_if<std::is_floating_point<T>::value, T>::type& data)
{
//Float specific
}
//Specialization
template <> bool Equal(const std::string& s, const Ip& data)
{
//Ip specific
}
int main()
{
//Equal("21",21); // Do not compile
Equal("21","42.5"); // Compile
}
but when trying to compile, the template functions with std::enable_if does not seem to participate in the resolution, and so the compiler tells me that there is no function that match my function call. I tried using std::enable_if with the return type, but no luck there either. I'm sure there is something I'm doing terribly wrong in this code, or maybe I'm trying to do this the wrong way. I'm trying to not write every int specialization (int, short, long, long long, ...), so does anyone have a solution for this ?
I'd make several changes to your code, beginning with the unconstrained function template. Since you're handling arithmetic types separately, that should only be selected if T is neither integral nor floating point.
template <typename T>
typename std::enable_if<!std::is_integral<T>::value &&
!std::is_floating_point<T>::value, bool>::type
Equal(const std::string& s, const T& data)
{return s == data;}
Now, the way you've defined Equal for integral and floating point types causes the T to be a non-deduced context. So move the enable_if to a dummy template parameter to allow deduction (you could also use the return type, as above).
template <typename T,
typename std::enable_if<std::is_integral<T>::value, T>::type* = nullptr>
bool Equal(const std::string& s, const T& data)
{
//int specific
return false;
}
template <typename T,
typename std::enable_if<std::is_floating_point<T>::value, T>::type* = nullptr>
bool Equal(const std::string& s, const T& data)
{
//Float specific
return false;
}
Finally, there's no need to specialize for handling Ip, create an overload instead.
//Overload
bool Equal(const std::string& s, const Ip& data)
{
//Ip specific
return false;
}
Live demo
Following works: (https://ideone.com/G1vJKm)
#include <string>
#include <type_traits>
struct Ip
{
std::string ip;
};
template <typename T>
typename std::enable_if<!std::is_integral<T>::value && !std::is_floating_point<T>::value, bool>::type
Equal(const std::string& s, const T& data)
{return s == data;}
template <typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type
Equal(const std::string& s, const T& data)
{
//int specific
return false;
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
Equal(const std::string& s, const T& data)
{
//Float specific
return false;
}
bool Equal(const std::string& s, const Ip& data)
{
//Ip specific
return false;
}
In your case,
template <typename T>
bool Equal(const std::string& s, const T& data)
is a valid candidate for all T, also for integral and floating point.
Your other template functions, compiler can't deduce type T for typename std::enable_if<std::is_integral<T>::value, T>::type.
And for the Ip version a simpler overload is sufficient (and preferred).