Im trying to define the spaceship operator for a simple template string class.
namespace mylib
{
template <size_t N>
struct String
{
String()
{
*data = '';
}
String(const char *str)
{
size_t l = strlen(str);
if (l >= N)
throw std::runtime_error(str);
memcpy(data, str, l + 1);
}
auto operator<=>(const String& str) const
{
return data <=> str.data;
}
char data[N];
};
}
void test_string()
{
mylib::String<16> str1;
mylib::String<16> str2("Hallo");
mylib::String<16> str3 = "Hallo";
mylib::String<16> str4 = "hallo";
assert(str2 == str3);
assert(str4 != str2);
assert(str2 < str4);
assert(str2 <= str4);
assert(str4 > str2);
assert(str4 >= str2);
mylib::String<3> str5 = "Hallo";
}
But i get some errors.
1>E:ProjectsWindowsindIndDlg.cpp(104,2): error C2678: binary '==': no operator found which takes a left-hand operand of type 'mylib::String<16>' (or there is no acceptable conversion)
1> 'bool operator ==(const D2D1_SIZE_U &,const D2D1_SIZE_U &)': cannot convert argument 1 from 'mylib::String<16>' to 'const D2D1_SIZE_U &'
How to define the spaceship operator for a template class?
It should be able to compare strings of different size:
String<16> < String<32>
for example.
I’m using Visual Studio 2022 C++ 20.
Thank you for all the comments!
Here is the new code including all includes and
the template comparison operator.
// String.cpp
#include <cassert>
#include <compare>
#include <stdexcept>
namespace mylib
{
template <size_t N>
struct String
{
String()
{
*data = '';
}
String(const char* str)
{
size_t l = strlen(str);
if (l >= N)
throw std::runtime_error(str);
memcpy(data, str, l + 1);
}
template<size_t N2>
auto operator<=>(const String<N2>& str)
{
return strcmp(data, str.data);
}
template<size_t N2>
bool operator==(const String<N2>& str) const
{
return operator<=>(str) == 0;
}
char data[N];
};
} // namespace mylib
int main()
{
mylib::String<16> str1;
mylib::String<16> str2("Hallo");
mylib::String<162> str3 = "Hallo";
mylib::String<16> str4 = "hallo";
assert(str2 == str3);
assert(str4 != str2);
assert(str2 < str4);
assert(str2 <= str4);
assert(str4 > str2);
assert(str4 >= str2);
mylib::String<3> str5 = "Hallo";
return 0;
}
Now im getting the following error 4 times:
1>E:ProjectsWindowsTestStringString.cpp(53,2): error C2666: ‘mylib::String<16>::operator <=>’: overloaded functions have similar conversions
1> E:ProjectsWindowsTestStringString.cpp(29,8):
1> could be ‘auto mylib::String<16>::operator <=><16>(const mylib::String<16> &)’ [rewritten expression ‘0 < (x <=> y)’]
1> E:ProjectsWindowsTestStringString.cpp(29,8):
1> or ‘auto mylib::String<16>::operator <=><16>(const mylib::String<16> &)’ [synthesized expression ‘(y <=> x) < 0’]
1> E:ProjectsWindowsTestStringString.cpp(53,2):
1> while trying to match the argument list ‘(mylib::String<16>, mylib::String<16>)’
What i am doing wrong?
Now i changed the data from char to array but still get the same errors as before.
// String.cpp
#include <array>
#include <cassert>
#include <compare>
#include <stdexcept>
namespace mylib
{
// This class is for short constant strings on the stack without any overhead.
template <size_t N>
struct String
{
String()
{
*data = '';
}
String(const char* str)
{
size_t l = strlen(str);
if (l >= N)
throw std::runtime_error(str);
memcpy(data, str, l + 1);
}
template<size_t N2>
auto operator<=>(const String<N2>& str)
{
return data <=> str.data;
}
template<size_t N2>
bool operator==(const String<N2>& str) const
{
return (data <=> str.data) == 0;
}
std::array<char, N> data;
};
} // namespace mylib
int main()
{
mylib::String<16> str1;
mylib::String<16> str2("Hallo");
mylib::String<32> str3 = "Hallo";
mylib::String<32> str4 = "hallo";
assert(str2 == str3);
assert(str4 != str2);
assert(str2 < str4);
assert(str2 <= str4);
assert(str4 > str2);
assert(str4 >= str2);
mylib::String<3> str5 = "Hallo";
return 0;
}
3
Answers
You can’t compare arrays, that’s completely wrong. Replace
return data <=> str.data;
with actual comparison algorithm. You may actually need separate != operation as a short-circuit.Each instance of
String<N>
with uniqueN
value is a unique type, so you have to have a template of operator:Another thing would be to have an ability to compare wita C-string. Which can’t be done by this operator, because a pointer C-string doesn’t carry size which could be used to construct
String
:And we may compare with array (or construct
String
from array):PS. You likely would need also a a constructor like that.Your string is also able to fail if source isn’t null-terminated,
strlen
will have ndefined behaviour.char data[N]
has no comparison operator defined, if you usestd::array<char, N>
instead your code will work as it does have a comparison operator.As a bonus as
std::array
is your only member you can just use a default operator:Note that though there’s no comparison operator defined for c-style arrays the default comparison operator is defined for array members so if using the default comparison operator you don’t have to change to
std::array
(though I’d still recommend usingstd::array
as it has other benefits over c-style arrays).When you overload the spaceship operator (<=>), it automatically provides default behavior for other relational operators (<, <=, >, >=) but not for operators (== and !=) by providing overloading for ==(it will also cover !=).
Also, spaceship operator does not work directly with arrays, it works with objects that have a defined spaceship operator.
This is updated code