Nie, memcmp
nie nadaje się do tego. Odbicie w C ++ jest w tym momencie niewystarczające (będą kompilatory eksperymentalne, które obsługują odbicie wystarczająco silne, aby to zrobić już, i c ++ 23 może mieć potrzebne funkcje).
Bez wbudowanego odbicia najłatwiejszym sposobem rozwiązania problemu jest ręczne odbicie.
Weź to:
struct some_struct {
int x;
double d1, d2;
char c;
};
chcemy wykonać minimalną ilość pracy, abyśmy mogli porównać dwa z nich.
Jeśli mamy:
auto as_tie(some_struct const& s){
return std::tie( s.x, s.d1, s.d2, s.c );
}
lub
auto as_tie(some_struct const& s)
-> decltype(std::tie( s.x, s.d1, s.d2, s.c ))
{
return std::tie( s.x, s.d1, s.d2, s.c );
}
dla c ++ 11 , a następnie:
template<class S>
bool are_equal( S const& lhs, S const& rhs ) {
return as_tie(lhs) == as_tie(rhs);
}
wykonuje całkiem przyzwoitą robotę.
Możemy rozszerzyć ten proces na rekurencyjny przy odrobinie pracy; zamiast porównywać więzi, porównaj każdy element zawinięty w szablon, a szablon operator==
rekurencyjnie stosuje tę regułę (zawijanie elementu w as_tie
celu porównania), chyba że element ma już działający==
, i nie obsługuje tablic.
Będzie to wymagało odrobiny biblioteki (100 linii wiersza kodu?) Wraz z zapisaniem odrobiny ręcznych danych „refleksyjnych” dla poszczególnych członków. Jeśli liczba posiadanych struktur jest ograniczona, łatwiejsze może być ręczne napisanie kodu dla struktury.
Prawdopodobnie istnieją sposoby na zdobycie
REFLECT( some_struct, x, d1, d2, c )
do generowania as_tie
struktury przy użyciu okropnych makr. Ale as_tie
to jest dość proste. W c ++ 11 powtórzenie jest denerwujące; jest to przydatne:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
w tej sytuacji i wielu innych. Dzięki RETURNS
, pisanie as_tie
jest:
auto as_tie(some_struct const& s)
RETURNS( std::tie( s.x, s.d1, s.d2, s.c ) )
usunięcie powtórzenia.
Oto próba uczynienia go rekurencyjnym:
template<class T,
typename std::enable_if< !std::is_class<T>{}, bool>::type = true
>
auto refl_tie( T const& t )
RETURNS(std::tie(t))
template<class...Ts,
typename std::enable_if< (sizeof...(Ts) > 1), bool>::type = true
>
auto refl_tie( Ts const&... ts )
RETURNS(std::make_tuple(refl_tie(ts)...))
template<class T, std::size_t N>
auto refl_tie( T const(&t)[N] ) {
// lots of work in C++11 to support this case, todo.
// in C++17 I could just make a tie of each of the N elements of the array?
// in C++11 I might write a custom struct that supports an array
// reference/pointer of fixed size and implements =, ==, !=, <, etc.
}
struct foo {
int x;
};
struct bar {
foo f1, f2;
};
auto refl_tie( foo const& s )
RETURNS( refl_tie( s.x ) )
auto refl_tie( bar const& s )
RETURNS( refl_tie( s.f1, s.f2 ) )
c ++ 17 refl_tie (tablica) (w pełni rekurencyjny, obsługuje nawet tablice tablic):
template<class T, std::size_t N, std::size_t...Is>
auto array_refl( T const(&t)[N], std::index_sequence<Is...> )
RETURNS( std::array<decltype( refl_tie(t[0]) ), N>{ refl_tie( t[Is] )... } )
template<class T, std::size_t N>
auto refl_tie( T(&t)[N] )
RETURNS( array_refl( t, std::make_index_sequence<N>{} ) )
Przykład na żywo .
Tutaj używam std::array
od refl_tie
. Jest to znacznie szybsze niż moja poprzednia krotka refl_tie w czasie kompilacji.
Również
template<class T,
typename std::enable_if< !std::is_class<T>{}, bool>::type = true
>
auto refl_tie( T const& t )
RETURNS(std::cref(t))
użycie std::cref
tutaj zamiast std::tie
może zaoszczędzić na nakładach czasu kompilacji, ponieważ cref
jest to znacznie prostsza klasa niż tuple
.
Na koniec powinieneś dodać
template<class T, std::size_t N, class...Ts>
auto refl_tie( T(&t)[N], Ts&&... ) = delete;
co zapobiegnie rozkładaniu się elementów tablicy na wskaźniki i cofaniu się do równości wskaźnika (czego prawdopodobnie nie chcesz od tablic).
Bez tego, jeśli przekażesz tablicę do struktury nie odzwierciedlonej, wraca ona do struktury wskaźnikowej do nie odzwierciedlonej refl_tie
, która działa i zwraca bzdury.
Z tego wynika błąd czasu kompilacji.
Obsługa rekurencji za pomocą typów bibliotek jest trudna. Mógłbyś std::tie
im:
template<class T, class A>
auto refl_tie( std::vector<T, A> const& v )
RETURNS( std::tie(v) )
ale to nie obsługuje rekurencji.