How to use forwarding to cast rvalue to lvalue reference? - c++

I'm trying to use an overloaded function to hide passing an int reference to a utility function when I don't care for the returned int data. Why is it that I need to use T& instead of T as the template argument? And why do I need the intermediate function that defines i as an rvalue?
class DataClass
{
};
template <typename T>
bool func(T& data, int& i) {
i = 1;
cout << "it worked!" << endl;
}
// I would like to remove this function
template <typename T>
bool func(T& data, int&& i) {
return func(std::forward<T&>(data), std::forward<int&>(i));
}
template <typename T>
bool func(T& data) {
// Why doesn't this line work?
// return func(std::forward<T>(data), std::forward<int>(0));
return func(std::forward<T&>(data), 0);
}
int main(int argc, char ** argv)
{
DataClass d;
func<DataClass>(d);
return 0;
}

You don't need std::forward at all here. Except for int&& i in the second overload, your parameters all declared as non-const lvalue references, so you can't pass an rvalue to any of them. And in the int&& overload, if you want to call the lvalue function from the rvalue function, you can just name the parameter i, because a name is always an lvalue.
template <typename T>
bool func(T& data, int& i) {
i = 1;
cout << "it worked!" << endl;
}
template <typename T>
bool func(T& data, int&& i) {
return func(data, i);
}
template <typename T>
bool func(T& data) {
return func(data, 0);
}
If you'd like to remove a function, note that it's actually the int& that does something effectively different: it changes i to 1. The rvalue overload technically also does, but something that has been passed as an rvalue should generally be ignored after that point, so callers should only count on func(T&, int&&) to print the message to cout. And to take an int that isn't an lvalue... just take an int.
template <typename T>
bool func(T& data, int& i) {
i = 1;
cout << "it worked!" << endl;
}
template <typename T>
bool func(T& data, int i=0) {
return func(data, i); // effect on i is ignored.
}
// Okay to pass a const int?
template <typename T>
bool func(T&, const volatile int&&) = delete;
That third deleted template preserves one behavior of your original code, though it's not clear if you actually want that behavior or not. In the function
void test() {
DataClass d;
const int n = 5;
func(d, n);
}
... the original code would have failed to compile, since a const int lvalue can't bind to either int& or int&&. But the change to a parameter of simply int would allow this test to compile, by making a copy of n to be the plain int parameter. Then the change to that int i just gets discarded, even though you gave n to the function. The deleted template is a better match for the const int lvalue n, so it would cause test to fail to compile. If you do want the behavior where func(d, n) is valid but has no effect on n, just take out that deleted template.

Related

Deducing a const l-value reference from a non-const l-value reference in C++ template

Suppose you have the following pair of functions:
void f(const int&) {
// Do something, making a copy of the argument.
}
void f(int&&) {
// Do the same thing, but moving the argument.
}
They are fairly redundant—the only difference between the functions being whether they copy or move their argument. Of course, we can do better by re-writing this as a single template function:
template<typename T>
void g(T&&) {
// Do something, possibly using std::forward to copy or move the argument.
}
This works, and is a commonly used idiom in practice. But the template might be instantiated into three functions, up from our two above. We can verify this occurs with the following piece of code:
#include <iostream>
template<typename T> constexpr char *type = nullptr;
template<> constexpr const char *type<int&> = "int&";
template<> constexpr const char *type<const int&> = "const int&";
template<> constexpr const char *type<int> = "int";
template<typename T>
void g(T&&) {
std::cout << reinterpret_cast<void*>(&g<T>)
<< " = &g<" << type<T> << ">" << std::endl;
}
int main() {
int i = 0;
const int& cr = 0;
g(i);
g(cr);
g(0);
return 0;
}
/*
Prints:
0x100f45080 = &g<int&>
0x100f45100 = &g<const int&>
0x100f45180 = &g<int>
*/
This has added a third function for the case when T = int&, which we didn't have when we were using our non-templated function f above. In this case, we don't actually need this non-const l-value reference version of the function—given f was sufficient for our original needs—and this increases the size of our code, especially if we have many template functions written this way that call each other.
Is there a way to write our function g above so that the compiler will automatically deduce T = const int& when g(i) is called in our example code? I.e., a way where we don't have to manually write g<const int&>(i) yet still get the desired behavior.
It is a subjective point-of-view to say "forward references" ("universal references") are better than dedicated overloads. There are certainly many cases where this is true, but if you want to have full control they won't do all the jobs.
You could explicitly make sure users do not pass non-const lvalue references, by adding
static_assert(!std::is_lvalue_reference<T>::value || std::is_const<typename std::remove_reference<T>::type>::value, "only call g with const argument");
inside g, but this is not in all cases a very good solution.
Or you do what is done for vector::push_back(...) and provide explicit overloads -- but this was your starting point, see https://en.cppreference.com/w/cpp/container/vector/push_back.
The 'correct' answer just depends on your requirements.
Edit:
the suggestion of #Sjoerd would look something like:
template <typename T>
class aBitComplicated {
public:
void func(T&& v) { internal_func(std::forward<T>(v)); }
void func(const T& v) { internal_func(v); }
private:
template <typename U>
void internal_func(U&& v) { /* your universal code*/ }
};
There also a bit more sophisticated/complicated version of this, but this here should be the most simple version to achieve what you asked for.

Forwarding reference: returning T when given T&& and T& when given T&

Consider a class A, how can I write a template having the same behaviour as
A& pretty(A& x)
{
/* make x pretty */
return x;
}
A pretty(A&& x)
{
/* make x pretty */
return x;
}
Knowing that I want to:
modify the argument (x) in the exact same way, independently of whether the parameter is an rvalue reference or an lvalue reference (in fact, the two sections /* make x pretty */ are identical), hence having a single function;
avoid unnecessary copying;
be able to use the function to modify a variable;
be able to "pipeline" the function call, no matter if the argument is an rvalue or an lvalue.
As an example for 3. and 4., consider the following use case:
void read_A(const A& x) { /* ... */ }
void take_A(A&& x) { /* ... */ }
A x();
read_A(pretty(x));
take_A(pretty(A()));
My idea was to take advantage of forwarding references while limiting the allowed parameters to references to A. But what about the return type?
template<typename T>
std::enable_if_t<std::is_same<T, A>::value>
/*???*/ pretty(T&& x)
{
/* make x pretty */
return x; //?
}
Why not simply write
#include <iostream>
template <class T>
T somefunc(T&& a) {
/* do something with a */
std::cout << __PRETTY_FUNCTION__ << '\n';
return std::forward<T>(a);
}
int main(int argc, char* argv[]) {
int a = 5;
somefunc(a);
somefunc(5);
return 0;
}
which will return
T somefunc(T &&) [T = int &]
T somefunc(T &&) [T = int]
As you can see, the functions have the signature you want. In the first call, T gets int &, and hence, the return value of int &. In the second, you have T = int = int &&, which is again what you want.
EDIT. By the way, your initial thought of applying std::is_same for disabling overload resolution for types other than A seems to be wrong, too. According to cppreference,
If T and U name the same type with the same const-volatile qualifications, provides the member constant value equal to true. Otherwise value is false.
Hence, you may want to use something like decay, or at least a combination of remove_cv and remove_reference to apply the logic you want. Below is the modified version of the above code, which now includes the overload resolution enabler.
#include <iostream>
#include <type_traits>
template <class T> T somefunc(T &&a) {
/* do something with a */
std::cout << __PRETTY_FUNCTION__ << '\n';
return std::forward<T>(a);
}
template <class T, typename std::enable_if<
std::is_same<typename std::decay<T>::type, float>::value,
int>::type = 0>
T onlyfloat_correct(T &&a) {
/* do something with a */
std::cout << __PRETTY_FUNCTION__ << '\n';
return std::forward<T>(a);
}
template <class T,
typename std::enable_if<std::is_same<T, float>::value, int>::type = 0>
T onlyfloat_wrong(T &&a) {
/* do something with a */
std::cout << __PRETTY_FUNCTION__ << '\n';
return std::forward<T>(a);
}
int main(int argc, char *argv[]) {
int a = 5;
const float b = 5;
somefunc(a);
somefunc(5);
onlyfloat_correct(b);
// onlyfloat_wrong(b);
return 0;
}
You don't need a template here at all. You can take advantage of the fact that a rvalue reference is an lvalue all call pretty in the rvalue reference overload so you only have to code the make x pretty logic in the lvalue overload. That looks like
A& pretty(A& x)
{
/* make x pretty */
return x;
}
A pretty(A&& x)
{
return std::move(pretty(x));
}

Why is std::forward necessary with forwarding references [duplicate]

This question already has answers here:
What are the main purposes of std::forward and which problems does it solve?
(7 answers)
Closed 6 years ago.
In a function template like this
template <typename T>
void foo(T&& x) {
bar(std::forward<T>(x));
}
Isn't x an rvalue reference inside foo, if foo is called with an rvalue reference? If foo is called with an lvalue reference, the cast isn't necessary anyway, because x will also be an lvalue reference inside of foo. Also T will be deduced to the lvalue reference type, and so std::forward<T> won't change the type of x.
I conducted a test using boost::typeindex and I get exactly the same types with and without std::forward<T>.
#include <iostream>
#include <utility>
#include <boost/type_index.hpp>
using std::cout;
using std::endl;
template <typename T> struct __ { };
template <typename T> struct prt_type { };
template <typename T>
std::ostream& operator<<(std::ostream& os, prt_type<T>) {
os << "\033[1;35m" << boost::typeindex::type_id<T>().pretty_name()
<< "\033[0m";
return os;
}
template <typename T>
void foo(T&& x) {
cout << prt_type<__<T>>{} << endl;
cout << prt_type<__<decltype(x)>>{} << endl;
cout << prt_type<__<decltype(std::forward<T>(x))>>{} << endl;
cout << endl;
}
int main(int argc, char* argv[])
{
foo(1);
int i = 2;
foo (i);
const int j = 3;
foo(j);
foo(std::move(i));
return 0;
}
The output of g++ -Wall test.cc && ./a.out with gcc 6.2.0 and boost 1.62.0 is
__<int>
__<int&&>
__<int&&>
__<int&>
__<int&>
__<int&>
__<int const&>
__<int const&>
__<int const&>
__<int>
__<int&&>
__<int&&>
Edit: I found this answer: https://stackoverflow.com/a/27409428/2640636 Apparently,
as soon as you give a name to the parameter it is an lvalue.
My question is then, why was this behavior chosen over keeping rvalue references as rvalues even when they are given names? It seems to me that the whole forwarding ordeal could be circumvented that way.
Edit2: I'm not asking about what std::forward does. I'm asking about why it's needed.
Isn't x an rvalue reference inside foo ?
No, x is a lvalue inside foo (it has a name and an address) of type rvalue reference. Combine that with reference collapsing rules and template type deduction rules and you'll see that you need std::forward to get the right reference type.
Basically, if what you pass to as x is a lvalue, say an int, then T is deduced as int&. Then int && & becomes int& (due to reference collapsing rules), i.e. a lvalue ref.
On the other hand, if you pass a rvalue, say 42, then T is deduced as int, so at the end you have an int&& as the type of x, i.e. a rvalue. Basically that's what std::forward does: casts to T&& the result, like a
static_cast<T&&>(x)
which becomes either T&& or T& due reference collapsing rules.
Its usefulness becomes obvious in generic code, where you may not know in advance whether you'll get a rvalue or lvalue. If you don't invoke std::forward and only do f(x), then x will always be a lvalue, so you'll be losing move semantics when needed and may end up with un-necessary copies etc.
Simple example where you can see the difference:
#include <iostream>
struct X
{
X() = default;
X(X&&) {std::cout << "Moving...\n";};
X(const X&) {std::cout << "Copying...\n";}
};
template <typename T>
void f1(T&& x)
{
g(std::forward<T>(x));
}
template <typename T>
void f2(T&& x)
{
g(x);
}
template <typename T>
void g(T x)
{ }
int main()
{
X x;
std::cout << "with std::forward\n";
f1(X{}); // moving
std::cout << "without std::forward\n";
f2(X{}); // copying
}
Live on Coliru
You really don't want your parameters to be automatically moved to the functions called. Consider this function:
template <typename T>
void foo(T&& x) {
bar(x);
baz(x);
global::y = std::forward<T>(x);
}
Now you really don't want an automatic move to bar and an empty parameter to baz.
The current rules of requiring you to specify if and when to move or forward a parameter are not accidental.
I get exactly the same types with and without std::forward<T>
...no? Your own output proves you wrong:
__<int> // T
__<int&&> // decltype(x)
__<int&&> // std::forward<T>(x)
Without using std::forward<T> or decltype(x) you will get int instead of int&&. This may inadvertently fail to "propagate the rvalueness" of x - consider this example:
void foo(int&) { cout << "int&\n"; }
void foo(int&&) { cout << "int&&\n"; }
template <typename T>
void without_forward(T&& x)
{
foo(x);
// ^
// `x` is an lvalue!
}
template <typename T>
void with_forward(T&& x)
{
// `std::forward` casts `x` to `int&&`.
// vvvvvvvvvvvvvvvvvv
foo(std::forward<T>(x));
// ^
// `x` is an lvalue!
}
template <typename T>
void with_decltype_cast(T&& x)
{
// `decltype(x)` is `int&&`. `x` is casted to `int&&`.
// vvvvvvvvvvv
foo(decltype(x)(x));
// ^
// `x` is an lvalue!
}
int main()
{
without_forward(1); // prints "int&"
with_forward(1); // prints "int&&"
with_decltype_cast(1); // prints "int&&"
}
wandbox example
x being an r-value is NOT the same thing as x having an r-value-reference type.
R-value is a property of an expression, whereas r-value-reference is a property of its type.
If you actually try to pass a variable that is an r-value reference to a function, it is treated like an l-value. The decltype is misleading you. Try it and see:
#include <iostream>
#include <typeinfo>
using namespace std;
template<class T> struct wrap { };
template<class T>
void bar(T &&value) { std::cout << " vs. " << typeid(wrap<T>).name() << std::endl; }
template<class T>
void foo(T &&value) { std::cout << typeid(wrap<T>).name(); return bar(value); }
int main()
{
int i = 1;
foo(static_cast<int &>(i));
foo(static_cast<int const &>(i));
foo(static_cast<int &&>(i));
foo(static_cast<int const &&>(i));
}
Output:
4wrapIRiE   vs. 4wrapIRiE
4wrapIRKiE vs. 4wrapIRKiE
4wrapIiE     vs. 4wrapIRiE (these should match!)
4wrapIKiE   vs. 4wrapIRKiE (these should match!)

How do I avoid implicit conversions on non-constructing functions?

How do I avoid implicit casting on non-constructing functions?
I have a function that takes an integer as a parameter,
but that function will also take characters, bools, and longs.
I believe it does this by implicitly casting them.
How can I avoid this so that the function only accepts parameters of a matching type, and will refuse to compile otherwise?
There is a keyword "explicit" but it does not work on non-constructing functions. :\
what do I do?
The following program compiles, although I'd like it not to:
#include <cstdlib>
//the function signature requires an int
void function(int i);
int main(){
int i{5};
function(i); //<- this is acceptable
char c{'a'};
function(c); //<- I would NOT like this to compile
return EXIT_SUCCESS;
}
void function(int i){return;}
*please be sure to point out any misuse of terminology and assumptions
Define function template which matches all other types:
void function(int); // this will be selected for int only
template <class T>
void function(T) = delete; // C++11
This is because non-template functions with direct matching are always considered first. Then the function template with direct match are considered - so never function<int> will be used. But for anything else, like char, function<char> will be used - and this gives your compilation errrors:
void function(int) {}
template <class T>
void function(T) = delete; // C++11
int main() {
function(1);
function(char(1)); // line 12
}
ERRORS:
prog.cpp: In function 'int main()':
prog.cpp:4:6: error: deleted function 'void function(T) [with T = char]'
prog.cpp:12:20: error: used here
This is C++03 way:
// because this ugly code will give you compilation error for all other types
class DeleteOverload
{
private:
DeleteOverload(void*);
};
template <class T>
void function(T a, DeleteOverload = 0);
void function(int a)
{}
You can't directly, because a char automatically gets promoted to int.
You can resort to a trick though: create a function that takes a char as parameter and don't implement it. It will compile, but you'll get a linker error:
void function(int i)
{
}
void function(char i);
//or, in C++11
void function(char i) = delete;
Calling the function with a char parameter will break the build.
See http://ideone.com/2SRdM
Terminology: non-construcing functions? Do you mean a function that is not a constructor?
8 years later (PRE-C++20, see edit):
The most modern solution, if you don't mind template functions -which you may mind-, is to use a templated function with std::enable_if and std::is_same.
Namely:
// Where we want to only take int
template <class T, std::enable_if_t<std::is_same_v<T,int>,bool> = false>
void func(T x) {
}
EDIT (c++20)
I've recently switched to c++20 and I believe that there is a better way. If your team or you don't use c++20, or are not familiar with the new concepts library, do not use this. This is much nicer and the intended method as outlines in the new c++20 standard, and by the writers of the new feature (read a papers written by Bjarne Stroustrup here.
template <class T>
requires std::same_as(T,int)
void func(T x) {
//...
}
Small Edit (different pattern for concepts)
The following is a much better way, because it explains your reason, to have an explicit int. If you are doing this frequently, and would like a good pattern, I would do the following:
template <class T>
concept explicit_int = std::same_as<T,int>;
template <explicit_int T>
void func(T x) {
}
Small edit 2 (the last I promise)
Also a way to accomplish this possibility:
template <class T>
concept explicit_int = std::same_as<T,int>;
void func(explicit_int auto x) {
}
Here's a general solution that causes an error at compile time if function is called with anything but an int
template <typename T>
struct is_int { static const bool value = false; };
template <>
struct is_int<int> { static const bool value = true; };
template <typename T>
void function(T i) {
static_assert(is_int<T>::value, "argument is not int");
return;
}
int main() {
int i = 5;
char c = 'a';
function(i);
//function(c);
return 0;
}
It works by allowing any type for the argument to function but using is_int as a type-level predicate. The generic implementation of is_int has a false value but the explicit specialization for the int type has value true so that the static assert guarantees that the argument has exactly type int otherwise there is a compile error.
Maybe you can use a struct to make the second function private:
#include <cstdlib>
struct NoCast {
static void function(int i);
private:
static void function(char c);
};
int main(){
int i(5);
NoCast::function(i); //<- this is acceptable
char c('a');
NoCast::function(c); //<- Error
return EXIT_SUCCESS;
}
void NoCast::function(int i){return;}
This won't compile:
prog.cpp: In function ‘int main()’:
prog.cpp:7: error: ‘static void NoCast::function(char)’ is private
prog.cpp:16: error: within this context
For C++14 (and I believe C++11), you can disable copy constructors by overloading rvalue-references as well:
Example:
Say you have a base Binding<C> class, where C is either the base Constraint class, or an inherited class. Say you are storing Binding<C> by value in a vector, and you pass a reference to the binding and you wish to ensure that you do not cause an implicit copy.
You may do so by deleting func(Binding<C>&& x) (per PiotrNycz's example) for rvalue-reference specific cases.
Snippet:
template<typename T>
void overload_info(const T& x) {
cout << "overload: " << "const " << name_trait<T>::name() << "&" << endl;
}
template<typename T>
void overload_info(T&& x) {
cout << "overload: " << name_trait<T>::name() << "&&" << endl;
}
template<typename T>
void disable_implicit_copy(T&& x) = delete;
template<typename T>
void disable_implicit_copy(const T& x) {
cout << "[valid] ";
overload_info<T>(x);
}
...
int main() {
Constraint c;
LinearConstraint lc(1);
Binding<Constraint> bc(&c, {});
Binding<LinearConstraint> blc(&lc, {});
CALL(overload_info<Binding<Constraint>>(bc));
CALL(overload_info<Binding<LinearConstraint>>(blc));
CALL(overload_info<Binding<Constraint>>(blc));
CALL(disable_implicit_copy<Binding<Constraint>>(bc));
// // Causes desired error
// CALL(disable_implicit_copy<Binding<Constraint>>(blc));
}
Output:
>>> overload_info(bc)
overload: T&&
>>> overload_info<Binding<Constraint>>(bc)
overload: const Binding<Constraint>&
>>> overload_info<Binding<LinearConstraint>>(blc)
overload: const Binding<LinearConstraint>&
>>> overload_info<Binding<Constraint>>(blc)
implicit copy: Binding<LinearConstraint> -> Binding<Constraint>
overload: Binding<Constraint>&&
>>> disable_implicit_copy<Binding<Constraint>>(bc)
[valid] overload: const Binding<Constraint>&
Error (with clang-3.9 in bazel, when offending line is uncommented):
cpp_quick/prevent_implicit_conversion.cc:116:8: error: call to deleted function 'disable_implicit_copy'
CALL(disable_implicit_copy<Binding<Constraint>>(blc));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Full Source Code: prevent_implicit_conversion.cc
Well, I was going to answer this with the code below, but even though it works with Visual C++, in the sense of producing the desired compilation error, MinGW g++ 4.7.1 accepts it, and invokes the rvalue reference constructor!
I think it must be a compiler bug, but I could be wrong, so – anyone?
Anyway, here's the code, which may turn out to be a standard-compliant solution (or, it may turn out that that's a thinko on my part!):
#include <iostream>
#include <utility> // std::is_same, std::enable_if
using namespace std;
template< class Type >
struct Boxed
{
Type value;
template< class Arg >
Boxed(
Arg const& v,
typename enable_if< is_same< Type, Arg >::value, Arg >::type* = 0
)
: value( v )
{
wcout << "Generic!" << endl;
}
Boxed( Type&& v ): value( move( v ) )
{
wcout << "Rvalue!" << endl;
}
};
void function( Boxed< int > v ) {}
int main()
{
int i = 5;
function( i ); //<- this is acceptable
char c = 'a';
function( c ); //<- I would NOT like this to compile
}
I first tried PiotrNycz's approach (for C++03, which I'm forced to use for a project), then I tried to find a more general approach and came up with this ForcedType<T> template class.
template <typename T>
struct ForcedType {
ForcedType(T v): m_v(v) {}
operator T&() { return m_v; }
operator const T&() const { return m_v; }
private:
template <typename T2>
ForcedType(T2);
T m_v;
};
template <typename T>
struct ForcedType<const T&> {
ForcedType(const T& v): m_v(v) {}
operator const T&() const { return m_v; }
private:
template <typename T2>
ForcedType(const T2&);
const T& m_v;
};
template <typename T>
struct ForcedType<T&> {
ForcedType(T& v): m_v(v) {}
operator T&() { return m_v; }
operator const T&() const { return m_v; }
private:
template <typename T2>
ForcedType(T2&);
T& m_v;
};
If I'm not mistaken, those three specializations should cover all common use cases. I'm not sure if a specialization for rvalue-reference (on C++11 onwards) is actually needed or the by-value one suffices.
One would use it like this, in case of a function with 3 parameters whose 3rd parameter doesn't allow implicit conversions:
function(ParamType1 param1, ParamType2 param2, ForcedType<ParamType3> param3);

Perfect Forwarding in C++03

If you have this function
template<typename T> f(T&);
And then try to call it with, let's say an rvalue like
f(1);
Why isn't T just be deduced to be const int, making the argument a const int& and thus bindable to an rvalue?
This is mentioned as a potential solution in the document I linked in the recent C++0x forwarding question.
It would work fairly well, but it breaks existing code. Consider (straight from the document):
template<class A1> void f(A1 & a1)
{
std::cout << 1 << std::endl;
}
void f(long const &)
{
std::cout << 2 << std::endl;
}
int main()
{
f(5); // prints 2 under the current rules, 1 after the change
int const n(5);
f(n); // 1 in both cases
}
Or
// helper function in a header
template<class T> void something(T & t) // #1
{
t.something();
}
// source
#include <vector>
void something(bool) // #2
{
}
int main()
{
std::vector<bool> v(5);
// resolves to #2 under the current rules, #1 after the change
something(v[0]);
}
This also fails to forward the value category (lvalue or rvalue), which isn't much of a problem in C++03. But since this fix could only be done during C++0x, we'd effectively shutting ourselves out from rvalue references when forwarding (a bad thing). We should strive for a better solution.
It is, but only if you declare f to take T const &.
template <typename T> void f(T &);
template <typename T> void g(T const &);
void x() { f(1); } // error: invalid initialization of non-const reference
void y() { g(1); } // no error
And if you declare both f(T &) and f(T const &), it'll choose the const-qualified one:
template <typename T> void f(T &);
template <typename T> void f(T const &);
void x() { f(1); } // no error, calls f(T const &)
Now maybe you're saying “in the first example, why does it generate a temporary of type int for the call to f when it could have generated a temporary of type const int and made the code compile?” The best answer I have for you is that that would be inconsistent with the overload resolution behavior when the argument isn't an integer constant.