Last active
August 29, 2015 14:02
-
-
Save mpark/2955020279470d2da354 to your computer and use it in GitHub Desktop.
A discussion with @sean-parent regarding passing an sink arguments of arbitrary type T by value.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* If we know that the sink argument is movable, | |
* then passing by value costs nothing if an rvalue is passed and | |
* only an extra move if an lvalue is passed. | |
* I agree that the extra move is no big deal here since it should be | |
* a constant time operation in most cases. | |
* | |
* So I like use cases such as: | |
* | |
* class TSink { | |
* public: | |
* | |
* TSink(std::string text) : Text(std::move(text)) {} | |
* | |
* private: | |
* | |
* std::string Text; | |
* | |
* }; // TSink | |
* | |
* However, if the sink argument is not movable, | |
* then passing by value costs nothing if an rvalue is passed but | |
* an extra __copy__ if an lvalue is passed. | |
* I'm not sure if an extra copy is acceptable? | |
* | |
* So my question is, do we still want this pattern for arbitrary Ts which | |
* may not be movable and therefore incur an extra copy rather than an extra move? | |
* | |
* // Copies twice if TVal is not movable on the way into the function, and | |
* // a move operation which falls back to copy. | |
* template <typename TVal> | |
* class TSink { | |
* public: | |
* | |
* TSink(TVal val) : Val(std::move(val)) {} | |
* | |
* private: | |
* | |
* TVal Val; | |
* | |
* }; // TSink<TVal> | |
* | |
* Note. This is what `model` looks like at: https://t.co/l1fnqjaOhD | |
**/ | |
#include <iostream> | |
// Non-movable object. | |
class TObj { | |
public: | |
TObj(int x) : X(x) { | |
std::cout << "TObj(" << X << ")" << std::endl; | |
} | |
TObj(const TObj &that) : X(that.X) { | |
std::cout << "TObj(const TObj &): " << X << std::endl; | |
} | |
~TObj() { | |
std::cout << "~TObj(" << X << ")" << std::endl; | |
} | |
int X; | |
}; // TObj | |
template <typename TVal> | |
class TSinkByValue { | |
public: | |
TSinkByValue(TVal val) : Val(std::move(val)) {} | |
private: | |
TVal Val; | |
}; // TSinkByValue | |
template <typename TVal> | |
class TSinkByForward { | |
public: | |
template <typename TForwardVal> | |
TSinkByForward(TForwardVal &&val) | |
: Val(std::forward<TForwardVal>(val)) {} | |
private: | |
TVal Val; | |
}; // TSinkByForward | |
int main() { | |
std::cout << "Sink by value, Rvalue." << std::endl; | |
TSinkByValue<TObj>{TObj{42}}; | |
std::cout << "Sink by forward, Rvalue." << std::endl; | |
TSinkByForward<TObj>{TObj{42}}; | |
std::cout << "Construct a TObj." << std::endl; | |
TObj obj(42); | |
std::cout << "Sink by value, Lvalue." << std::endl; | |
TSinkByValue<TObj>{obj}; | |
std::cout << "Sink by forward, Lvalue." << std::endl; | |
TSinkByForward<TObj>{obj}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Right, I did consider the fact that it enforces requirements on the types. I thought practically speaking perhaps they are okay. I shall read Elements of Programming in the near future.
I have indeed been following the discussion regarding destructive move and your blog post in specific. In general I share your opinion that we should first focus on utilizing other forms of hardware.
Thanks for the discussion Sean, I really appreciate your feedback.