I'm new to modern c++, and got messed up with value categories.
There is discusssion about 'forward<T> vs static_cast<T&&>'
Which says the purpose of using forward is to write clean code.
My question is
What are the differences of static_cast<T> and static_cast<T&&> if they are used in template?
Probably this is extended question. Why the direct use of static_cast<T> works fine unlike static_cast<T> in customized _forward function?
#include <iostream>
using namespace std;
void printInt(int& i) { cout << "lvalue reference: " << i << endl; }
void printInt(int&& i) { cout << "rvalue reference: " << i << endl; }
/* std::forward
*
* template<class _Ty>
* _NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept
* { // forward an lvalue as either an lvalue or an rvalue
* return (static_cast<_Ty&&>(_Arg));
* }
*
* template<class _Ty>
* _NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept
* { // forward an rvalue as an rvalue
* static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
* return (static_cast<_Ty&&>(_Arg));
* }
*/
template<class T>
T&& _forward(typename remove_reference<T>::type& arg) {
return static_cast<T>(arg);
}
template<typename T>
void test(T&& x) {
printInt(x); // x itself is lvalue
printInt(std::move(x)); // convert x into rvalue
printInt(std::forward<T>(x)); // inference reference type
printInt(static_cast<T&&>(x));
printInt(_forward<T>(x));
printInt(static_cast<T>(x));
}
int main() {
int a = 5;
cout << "pass lvalue" << endl;
test(a);
cout << "pass rvalue" << endl;
test(10);
}
[output]
pass lvalue
lvalue reference: 5
rvalue reference: 5
lvalue reference: 5
lvalue reference: 5
lvalue reference: 5
lvalue reference: 5
pass rvalue
lvalue reference: 10
rvalue reference: 10
rvalue reference: 10
rvalue reference: 10
rvalue reference: 17823636
rvalue reference: 10
printInt(x); // x itself is lvalue
printInt(std::move(x)); // convert x into rvalue
printInt(std::forward<T>(x)); // inference reference type
printInt(static_cast<T&&>(x));
works as expected.
printInt(_forward<T>(x));
printInt(static_cast<T>(x));
However, why does static_cast<T>(x) works same with static_cast<T&&>(x)?
It seems like it should print out rvalue only.
And why the case using _forward prints dummy value?
I just guess, maybe, xvalue is relevant with these results. But cannot explain clearly.
Related
Sincere apologies if this has been answered elsewhere, I did search but couldn't find a clear match.
I have a variadic function in a template class that does not work exactly as I expected. I have a workaround, but I suspect it's not the best solution.
Consider the following code:
#include <iostream>
#include <functional>
#include <vector>
template< typename... ARGS >
class Kitten
{
public:
using Callback = std::function< void( ARGS&&... ) >;
Kitten() = default;
void addCallback( Callback && c )
{
callbacks.push_back( std::forward< Callback >( c ) );
}
void processCallbacks( ARGS &&... args )
{
for ( Callback const &c : callbacks )
{
c( std::forward< ARGS >( args )... );
}
}
private:
std::vector< Callback > callbacks;
};
int main( int argc, const char * argv[] )
{
( void ) argc;
( void ) argv;
Kitten<int, float> kitty;
kitty.addCallback( []( int i, float f )
{
std::cout << "Int: " << i << "\nFloat: " << f << "\n";
} );
kitty.processCallbacks( 2, 3.141f );
int ii = 54;
float ff = 2.13f;
kitty.processCallbacks( ii, ff );
return 0;
}
This will not compile, the second call to processCallbacks will generate an error (clang, similar issue seen on vc14).
I can fix the compilation and get things working as expected if I change the definition of processCallbacks to:
template< typename... FORCEIT >
void processCallbacks( FORCEIT &&... args )
{
for ( Callback const &c : callbacks )
{
c( std::forward< ARGS >( args )... );
}
}
It seems to me to be a bit of a cheesy workaround even if it seems to work, and I suspect I'm missing a better solution.
My understanding of why the first sample fails is because there's no type deduction being done on the argument pack, so the compiler isn't generating the correct code for all cases. The second sample works because it forces the type deduction on the argument pack.
It's been puzzling me for a while on and off. Any help much appreciated.
edit: vc12 compiler error:
error C2664: 'void Kitten<int,float>::processCallbacks(int &&,float &&)' : cannot convert argument 1 from 'int' to 'int &&'
edit: Apple LLVM version 7.0.0 compiler error:
error: rvalue reference to type 'int' cannot bind to lvalue of type 'int'
Regarding the change suggested in the comments to use std::move, addCallback would seem to be even more flexible in the form:
template< typename FUNCTION >
void addCallback( FUNCTION && c )
{
callbacks.emplace_back( std::forward< FUNCTION >( c ) );
}
Using std::forward because the function now takes a universal reference.
As this would allow the following to work:
std::function< void( int, float )> banana( []( int i, float f )
{
std::cout << "Int: " << i << "\nFloat: " << f << "\n";
} );
kitty.addCallback( banana );
void processCallbacks( ARGS &&... args )
{
//...
}
For each type in ARGS the allowed value categories will be set by the template arguments to Kitten. E.g. for Kitten<float, int&, const bool&>, processCallbacks will accept rvalues for the first parameter, lvalues for the second (due to reference collapsing rules) and both for the third (because rvalues can bind to const lvalue references). This will not give you perfect forwarding.
template< typename... FORCEIT >
void processCallbacks( FORCEIT &&... args )
{
//...
}
That function template accepts both lvalues and rvalues because there is type deduction. Such a parameter is known as a forwarding reference parameter. Forwarding reference parameters must be of the form T&&, where T is some deduced template parameter.
If you want to accept lvalues and rvalues and perfect-forward them, you should use the latter form. Although it might seem weird to you at first, this is a fairly common pattern.
I have the following use of std::reference_wrapper for a build in type (double) and for a user defined type (std::string).
Why do they behave differently in the case of the stream operator?
#include<functional> //reference wrapper
#include<iostream>
void fd(double& d){}
void fs(std::string& s){}
int main(){
double D = 5.;
std::reference_wrapper<double> DR(D);
std::cout << "DR = " << DR << std::endl; //ok
fd(DR); // ok
std::string S = "hello";
std::reference_wrapper<std::string> SR(S);
std::cout << "SR = " << static_cast<std::string&>(SR) << std::endl; // ok
std::cout << "SR = " << SR << std::endl; // error: invalid operands to binary expression ('basic_ostream<char, std::char_traits<char> >' and 'std::reference_wrapper<std::string>')
fs(SR); // ok
}
http://coliru.stacked-crooked.com/a/fc4c614d6b7da690
Why in the first case DR is converted to double and printed and in the second it is not? Is there a work around?
Ok, I see now, in the ostream case I was trying to called a templated function that is not resolved:
#include<functional> //reference wrapper
void double_fun(double const& t){};
template<class C>
void string_fun(std::basic_string<C> const& t){};
int main(){
double D = 5.;
std::reference_wrapper<double> DR(D);
double_fun(DR); //ok
std::string S = "hello";
std::reference_wrapper<std::string> SR(S);
string_fun(SR); // error: no matching function for call to 'string_fun'
string_fun(SR.get()); // ok
string_fun(static_cast<std::string&>(SR)); // ok
string_fun(*&SR); // would be ok if `std::reference_wrapper` was designed/coded differently, see http://stackoverflow.com/a/34144470/225186
}
For the first part TC gave you the answer. That is, operator<< for basic_string is templated, and template argument deduction doesn't look through implicit conversions.
You could alternatively call SR.get() if you don't want to explicitly to static_cast your reference wrapper.
Now for the second part, string_fun takes as input arguments std::basic_string<C> objects. When you call:
string_fun(SR);
with SR as input parameter which is of type std::reference_wrapper<std::string>, naturally you get a type mismatch.
What you can do is provide an additional overload:
template<class C>
void string_fun(std::reference_wrapper<std::basic_string<C>> const& t) {
};
Live Demo
Or if you want a more unified treatment you could define your string_fun to take template template arguments, and resolve the type with some kind of type trait magic like bellow:
template<template<typename...> class C, typename T>
void
string_fun(C<T> const &t) {
std::cout <<
static_cast<std::conditional_t<
std::is_same<
std::reference_wrapper<T>, C<T>>::value, T, std::basic_string<T>>>(t) << std::endl;
}
Live Demo
As I understand it, one cannot change the reference variable once it has been initialized. See, for instance, this question. However, here is a minmal working example which sort of does reassign it. What am I misunderstanding? Why does the example print both 42 and 43?
#include <iostream>
class T {
int x;
public:
T(int xx) : x(xx) {}
friend std::ostream &operator<<(std::ostream &dst, T &t) {
dst << t.x;
return dst;
}
};
int main() {
auto t = T(42);
auto q = T(43);
auto &ref = t;
std::cerr << ref << std::endl;
ref = q;
std::cerr << ref << std::endl;
return 0;
}
You're not changing the reference here.
You are replacing the object the reference is referring to.
In other words: after the assignment, your t is replaced by q.
ref is still a reference to t.
That does not perform a reference reassignment. Instead, it copy assigns the object in variable q into the object referenced by ref (which is t in your example).
This also justifies why you got 42 as output: the default copy assignment operator modified the first object.
I have implemented a logging class TLogFile and now I want to overload the output operator<<.
I want to use the log like this:
TLogFile* log = new TLogFile("some arguments...");
*log << "Hello world."; // (1)
*log << "Hello world." << endl; // (2)
*log << std::hex << setw(2) << setfill('0') << someValue << endl; // (3)
I used ostream as a class member and as a friend. The class looks like this:
namespace app {
class TLogFile
{
public:
app::TLogFile& operator<< (std::string& out);
std::ostream& operator<< (std::ostream& out);
friend std::ostream& operator<< (std::ostream& out, TLogFile& o);
};
} // namespace app
Only plain text (1) is working by using the string version. A soon as I use endl (2) or iomanip (3) I get error messages:
../src/main.cpp:164:70: error: no match for 'operator<<' in 'sysdat.app::cSystemData::obj.app::cSystemObjects::applicationLog->app::TLogFile::operator<<((* & std::basic_string(((const char*)"sysdat.obj.applicationLog <<"), ((const std::allocator*)(& std::allocator()))))) << std::endl'
../src/main.cpp:164:70: note: candidates are:
../src/inc/logger.h:85:17: note: app::TLogFile& app::TLogFile::operator<<(const string&)
../src/inc/logger.h:85:17: note: no known conversion for argument 1 from '' to 'const string& {aka const std::basic_string&}'
../src/inc/logger.h:88:17: note: std::ostream& app::TLogFile::operator<<(std::ostream&)
../src/inc/logger.h:88:17: note: no known conversion for argument 1 from '' to 'std::ostream& {aka std::basic_ostream&}'
../src/inc/logger.h:93:23: note: std::ostream& app::operator<<(std::ostream&, app::TLogFile&)
../src/inc/logger.h:93:23: note: no known conversion for argument 1 from 'app::TLogFile' to 'std::ostream& {aka std::basic_ostream&}'
I believed that one of the ostream version should work.
Has anyone an idea how to overload the operator so that endl and iomanip can be used?
Your operator<< is able to take only std::ostream& and std::string&
(note: probably it should be const std::string&).
The most elegant solution I can imagine is to write a template:
class TLogFile{
protected:
std::ostream* stream;
public:
/* default ctor, copy ctor and assignment operator: */
TLogFile(std::ostream& _stream=std::clog):stream(&_stream){}
TLogFile (const TLogFile&) =default;
TLogFile& operator= (const TLogFile&) =default;
/* std::endl is overloaded,
* so I think compiler doesn't know which version to use.
* This funchtion handles function pointers, including std::endl
*/
inline TLogFile& operator<< (std::ostream&(*func)(std::ostream&)){
(*stream) << func;
return *this;
}
/* should handle everything else */
template<typename T>
inline TLogFile& operator<< (const T& t) {
(*stream) << t;
return *this;
}
}
See it working in online compiler
This way your objects' operator<<s should be able to take anything that std::ostream's can take.
Edit:
Next time, please say that you want to have custom std::endl.
I'm not sure that function with signature
inline TLogFile& operator<< (std::ostream&(*func)(std::ostream&))
is used only when std::endl is passed to it. My previous solution seems inelegent or even inworking. I'm wondering about how to change behaviour of std::endl when it's passed to object of different class.
Notes:
In most cases I'd like to use '\n instead of std::endl.
TLogFile* log = new TLogFile("some arguments...");
I think using raw pointer isn't the best idea here (it's easy to forget about delete),
unless you have to explicitly decide when the object should die.
When the object should die when the current scope does, it should be a local variable:
TLogFile log("some arguments...");
//Usage:
log << "Hello world."; // (1)
log << "Hello world." << endl; // (2)
log << std::hex << setw(2) << setfill('0') << someValue << endl; // (3)
If the object is used in multiple places, and each of the places uses it independently from others, IMO the best solution is to use std::shared_ptr:
#include <memory>
#include <utility>
auto log=std::make_shared<TLogFile>("some arguments...");
//Usage:
*log << "Hello world."; // (1)
*log << "Hello world." << endl; // (2)
*log << std::hex << setw(2) << setfill('0') << someValue << endl; // (3)
This way the object dies when the last shared_ptr does.
I used pointer in the class to be able to re-assign it. If you don't need re-assignment, you can use reference instead.
Thanks to GingerPlusPlus. I found out, that the operator operator<< (std::ostream&(*func)(std::ostream&)) is called only once for the endl (maybe this assumtion is not always true, Please read remarks/edit above of GingerPlusPlus). I replaced the ostream against a stringstream and write the contens of the stringstream when the operator ist called.
class TLogFile{
protected:
std::ostream* stream;
std::stringstream line;
public:
/* default ctor, copy ctor and assignment operator: */
TLogFile(std::ostream& _stream=std::clog):stream(&_stream){}
TLogFile (const TLogFile&) =default;
TLogFile& operator= (const TLogFile&) =default;
void write() {
// Doing some write stuff
// ...
// Empty stringstream buffer
line.str(std::string());
}
/* std::endl is overloaded,
* so I think compiler doesn't know which version to use.
* This funchtion handles function pointers, including std::endl
*/
inline TLogFile& operator<< (std::ostream&(*func)(std::ostream&)){
line << func;
write();
return *this;
}
/* should handle everything else */
template<typename T>
inline TLogFile& operator<< (const T& t) {
line << t;
return *this;
}
}
An object can be captured by mutable reference, and changed inside a member function which takes the same object as const.
void g(const int& x, std::function<void()> f)
{
std::cout << x << '\n';
f();
std::cout << x << '\n';
}
int main()
{
int y = 0;
auto f = [&y] { ++y; };
g(y, f);
}
An object is mutated in a scope where it is const. I understand that the compiler can't enforce constness here without proving that x and y are aliases. I suppose all I'm looking for is confirmation that this is undefined behavior. Is it equivalent in some sense to a const_cast - using a value as non-const in a context where it should be?
Reference or pointer to const doesn't mean the referenced object cannot be modified at all - it just means that the object cannot be modified via this reference/pointer. It may very well be modified via another reference/pointer to the same object. This is called aliasing.
Here's an example that doesn't use lambdas or any other fancy features:
int x = 0;
void f() { x = 42; }
void g(const int& y) {
cout << y;
f();
cout << y;
}
int main() {
g(x);
}
There's nothing undefined going on, because the object itself is not const, and constness on aliases is primarily for the user's benefit. For thoroughness, the relevant section is [dcl.type.cv]p3:
A pointer or reference to a cv-qualified type need not actually point
or refer to a cv-qualified object, but it is treated as if it does; a
const-qualified access path cannot be used to modify an object even if
the object referenced is a non-const object and can be modified
through some other access path. [ Note: Cv-qualifiers
are supported by the type system so that they cannot be subverted without casting (5.2.11). —end note ]