admin管理员组文章数量:1335600
Is the following Move operations legit, as in there is no UB. Instead of two swaps I want to implement a one swap of the 16 Bytes of MyString.
class MyString {
std::size_t size_;
char* str_;
MyString(const char* str, std::size_t len): size_(len), str_(size_ ? new char[size_+1] : nullptr) {
if (size_) {
memcpy(str_, str, len+1);
}
}
void bitwise_swap(MyString* l, MyString* r) {
constexpr size_t size = sizeof(MyString);
alignas (MyString) char tmp[size]; // is alignas even necessary?
std::memcpy(&tmp, l, size);
std::memcpy(l, r, size);
std::memcpy(r, &tmp, size);
}
public:
MyString(const char* str = ""): MyString(str, strlen(str)) {
}
MyString(MyString&& s) noexcept : size_(0), str_(nullptr) {
bitwise_swap(this, &s);
}
MyString& operator=(MyString&& s) {
bitwise_swap(this, &s);
return *this;
}
~MyString() {
delete[] str_;
}
};
Note that MyString is clearly not trivially copyable. However I'm always swapping the internal bit representation of one MyString object with another.
Also, I'm fine with the operator= swaping with it's parameter. If xvalue is received (i.e lvalue that was std::moved, I'm fine with it carrying my "garbage"). So, please compare to this versions:
MyString(MyString&& s) noexcept : size_(0), str_(nullptr) {
std::swap(size_, s.size_);
std::swap(str_, s.str_);
}
And:
MyString& operator=(MyString&& s) {
std::swap(size_, s.size_);
std::swap(str_, s.str_);
return *this;
}
If this code is valid. Some would (rightfully) say this is a premature optimization, but can anyone claim one version is usually more efficient than the other or that typical compilers typically do this kind of optimization themselves? i.e turn the two swaps of 8Bytes into one swap of 16Bytes.
This version is defined according to the standard: But isn't the straightforward code also valid? Can anyone quote the standard to prove it is valid or that it is not?
Is the following Move operations legit, as in there is no UB. Instead of two swaps I want to implement a one swap of the 16 Bytes of MyString.
class MyString {
std::size_t size_;
char* str_;
MyString(const char* str, std::size_t len): size_(len), str_(size_ ? new char[size_+1] : nullptr) {
if (size_) {
memcpy(str_, str, len+1);
}
}
void bitwise_swap(MyString* l, MyString* r) {
constexpr size_t size = sizeof(MyString);
alignas (MyString) char tmp[size]; // is alignas even necessary?
std::memcpy(&tmp, l, size);
std::memcpy(l, r, size);
std::memcpy(r, &tmp, size);
}
public:
MyString(const char* str = ""): MyString(str, strlen(str)) {
}
MyString(MyString&& s) noexcept : size_(0), str_(nullptr) {
bitwise_swap(this, &s);
}
MyString& operator=(MyString&& s) {
bitwise_swap(this, &s);
return *this;
}
~MyString() {
delete[] str_;
}
};
Note that MyString is clearly not trivially copyable. However I'm always swapping the internal bit representation of one MyString object with another.
Also, I'm fine with the operator= swaping with it's parameter. If xvalue is received (i.e lvalue that was std::moved, I'm fine with it carrying my "garbage"). So, please compare to this versions:
MyString(MyString&& s) noexcept : size_(0), str_(nullptr) {
std::swap(size_, s.size_);
std::swap(str_, s.str_);
}
And:
MyString& operator=(MyString&& s) {
std::swap(size_, s.size_);
std::swap(str_, s.str_);
return *this;
}
If this code is valid. Some would (rightfully) say this is a premature optimization, but can anyone claim one version is usually more efficient than the other or that typical compilers typically do this kind of optimization themselves? i.e turn the two swaps of 8Bytes into one swap of 16Bytes.
This version is defined according to the standard: https://compiler-explorer/z/xanvffGbG But isn't the straightforward code also valid? Can anyone quote the standard to prove it is valid or that it is not?
Share Improve this question edited Nov 26, 2024 at 12:29 dwto asked Nov 19, 2024 at 22:55 dwtodwto 8074 silver badges11 bronze badges2 Answers
Reset to default 2Note that MyString is clearly not trivially copyable. However I'm always swapping the internal bit representation of one MyString object with another.
std::memcpy
on a non-trivially-copyable type is UB. Specifically [basic.types.general]/2 and [basic.types.general]/3 specify the behavior of copying underlying bytes from an object to a character array and back to an object of the same type only for trivially-copyable types.
Even if the type was trivially-copyable, it would not generally be defined behavior. The mentioned paragraphs additionally require that neither object is potentially-overlapping, meaning for example that they must not be base class subobjects.
For an example why the last part is important: A derived class might reuse tail padding of the base class and in that case memcpy
would incorrectly overwrite the tail padding of the derived object.
Swapping the individual members one-by-one on the other hand is fine. (That would be the case even if you use memcpy
, because they are trivially-copyable and not potentially-overlapping).
Note that MyString is clearly not trivially copyable. However I'm always swapping the internal bit representation of one MyString object with another.
Your memcpy is equivalent to copying the two trivially-copyable data members (and only them - there should be no padding on any common system as size_t and pointers are both basically counts of bytes in the address space and should have the same bit width, there's no base class or vtable pointer...), so it has defined behaviour.
That said, I'd hazard that memcpy is (an ugly hack and) very unlikely to be faster. But, why trust our gut instinct when you can try it for the compilers/version/optimisation-flags you care about on Compiler Explorer - https://godbolt./ - and see for yourself?
本文标签: cImplementing bitwise swap for MyStringStack Overflow
版权声明:本文标题:c++ - Implementing bitwise swap for MyString - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742393806a2466515.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论