The post here points out that std::string
's member operator=
is not lvalue ref-qualified. That allows us to write code such as this:
std::string() = "Hello";
The linked post asks why this is allowed. The answer is backwards compatibility:
Changing the definition of something in the standard library is not something to be undertaken lightly, as it could needlessly break existing code.
This is not unreasonable. Perhaps someone has written code such as
// Does useful stuff
// Does useful stuff
std::string{} = "Hello"; // Does nothing
// Does useful stuff
// Does useful stuff
by accident. Making std::string::operator=
lvalue ref-qualified would prevent this old code from compiling, even if the program performs exactly as the programmer wants it to.
In the linked post, the user asks for a use-case, but one is not given. That is the question of this post.
The question
Is there a line of code that both
- would not compile with
std::string::operator=
lvalue ref-qualified
and
- if deleted would change the behavior of the program it is located in?
Note
Of course, by line of code, I do not mean code such as
std::string{} = "Hello"; i;
CodePudding user response:
Before shrink_to_fit
was available, a common idiom to make sure a string's memory was free'd was to swap your string with a temporary one:
std::string().swap(some_string);
I guess someone could have written this post C 11 using the assignment to temporary as well:
std::string() = std::move(some_string);
So this would no longer compile if the overload was only l-value qualified.
Also, IIRC shrink_to_fit
is non-binding, so you essentially still have to do this to guarantee deallocation.
CodePudding user response:
Almost all know why templates can only be implemented in the header file. This is a rule, but there are exceptions, as always.
Suppose someone wants to develop a template class library and whishes to hide library's code and restrict its templates for using only with types int
and float
. They design the header file:
// library.h
#include <string>
template <typename T>
struct Int {
operator std::string() const; // implicit stringifier
const T& get() const;
private:
T n;
};
They implement the template class member in the library.
// library.cc
#include "library.h"
template <typename T>
Int<T>::operator std::string() const & { a = n; return ""; }
Some other uses the library.
#include <iostream>
#include "iostream"
int main() {
Int<int> i;
std::cout << static_caststd::string(i);
}
They get linker errors:
main.cpp:8: undefined reference to `Int<int>::operator std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >[abi:cxx11]() const'
The class member functions were not called in the library, thus were not instantiated. The solution is calling them (with forcing side effects if needed.)
// library.cc
#include "library.h"
namespace {
int a;
}
template <typename T>
__attribute__ ((noinline)) Int<T>::operator std::string() const { a = n; return ""; }
int init() {
std::string() = Int<float>();
std::string() = Int<int>();
return 0;
}
int inited = init();
See the example https://godbolt.org/z/Y95PbTYf9.
std::string() = Int<float>();
was short and legal in C 98, for sure it was used and the committee wanted to keep the compatibility by not accepting the restrictions.