#include <cstdio>
#include <string>
class A {
std::string data;
public:
A() = default;
explicit A (const char* data) : data(data) {}
operator const char* () const;
explicit operator std::string() &&;
};
A::operator const char*() const {
printf("A -> const char*n");
return data.c_str();
}
A::operator std::string() && {
printf("A -> std::stringn");
return data;
}
int main() {
A a("lorem");
std::string s(std::move(a));
printf("%sn", s.c_str());
return 0;
}
The above code prints “A -> std::string” on gcc13 and “A -> const char*” on gcc14. Clang (18.1.8) does the same thing as gcc13. All compilers were called with -Wall -pedantic --std=c++17
The code is a minimal reproduction case.
I’m trying to add std::string
support to a custom class A
in a very old project. Unfortunately A
must have the implicit conversion to a c-str to avoid breaking existing code.
I had a working version but it’s broken on gcc14.
What I want is for the move conversion operator to be called when a call site attempts to construct std::string
from an rvalue reference of A. All of c-cast, function-cast, static_cast and initialization worked on gcc13.
What I think is happening is that on gcc13, the move conversion is called, as expected, and only a move occurs. On gcc14, a two step implicit conversion of is taken, using the implicit A::operator const char*()
and std::string(const char*)
. This creates the second string using a copy instead of a move.
Naming the conversion operator like C c = std::move(a).operator std::string();
does call the desired conversion.
Is there a way to convince gcc14 of the desired behavior?