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? Eg 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.
eg
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);
}
}
As visible above only
flr1<T>(std::forward<T>(arg))
orflr3<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
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.