Say that one has two classes which are different, but are somewhat "equivalent" in their purpose, so they have the same members, and one can be assigned by the other:
struct Pure
{
QString name;
int age = 18;
};
struct Observable
{
QProperty<QString> name;
QProperty<int> age {18};
void operator=(const Pure& other)
{
name = other.name;
age = other.age;
}
};
QProperty
is a template class from Qt, but I think the question is Qt-independent, as it would apply to any other template <typename T> class Foo
which has a conversion/assignment to/from T
.
The question is how to avoid the duplication, so one cannot so easily add or remove a member from one class and forget doing it on the other, while still retaining the possibility of assigning an object of one class to another.
I've attempted several things with templates, and this seems the most promising:
// Adding two helpers only available in C 20 for convenience.
template<typename T>
struct type_identity { using type = T; };
template<typename T>
using type_identity_t = typename type_identity<T>::type;
template<template<typename T> typename T>
struct State
{
T<QString> name;
T<int> age;
// std::enable_if_t<std::is_same<QProperty<class X>, T>>
// operator=(const State<type_identity_t>& other)
// {
// name = other.name;
// age = other.age;
// }
};
int main()
{
State<type_identity_t> state;
state.age = 42;
State<QProperty> observableState;
observableState = state; // Desired use. Fails to compile without operator=
}
The commented out code doesn't compile (error: use of template template parameter 'T' requires template arguments), with the error at the T
at the end of the first commented line, but I don't know how to fix this.
CodePudding user response:
You can't use std::is_same<QProperty<class X>, T>
, because this is trying to pass T
as the second argument to is_same
, but it's a template and is_same
is expecting a type.
You can make a is_same
-like trait that takes template classes instead:
template<template<typename...> class A, template<typename...> class B>
struct is_same_template_class : std::false_type {};
template<template<typename...> class T>
struct is_same_template_class<T, T> : std::true_type {};
// use `is_same_template_class<T, QProperty>`
Currently, there doesn't seem to be a reason to limit what your operator=
can do. Why not something like:
template<template<typename> class T>
struct State
{
T<QString> name;
T<int> age;
template<template<typename> class U>
State& operator=(const State<U>& other)
{
name = other.name;
age = other.age;
return *this;
}
};
And I am not familiar with QT, but it seems like using Observable = QProperty<Pure>;
may also work if you refactor some of your logic.