Отец C++, Бьярне Строуструп, упоминает в A Tour of C++, что можно использовать объявление структурированного связывания с std::complex
, который является не-POD типом (он инкапсулирует атрибуты данных, значения получаются с помощью вызовов методов real()
и imag()
). На сегодняшний день это фактически не работает для std::complex
(проверено с помощью Compiler Explorer), также в рабочем проекте стандарта WG21 не упоминается эта возможность std::complex
. Но это возможно, если сделать std::complex
кортежеподобным типом, включив такие правила (см. структурированное связывание):
- для каждого идентификатора выражение
std::tuple_element<i, std::complex<T>>::type
, гдеi
— индекс идентификатора константного выражения, должно быть типами идентификаторов, - для каждого идентификатора функция
get<i>(c)
, гдеi
— индекс идентификатора постоянного выражения, аc
— сложный объект, должна предоставлять значение идентификаторов.
Нам просто нужно расширить пространство имен std
и предоставить все ингредиенты.
namespace std {
template<typename T>
class tuple_size<complex<T>> {
public:
static constexpr size_t value = 2;
};
template<size_t I, typename T>
auto get(const complex<T>& c) {
if constexpr (I == 0) return c.real();
else return c.imag();
}
template <size_t I, typename T>
class tuple_element<I, complex<T>> {
public:
using type = decltype(get<I>(declval<complex<T>>()));
};
}
Теперь это хорошо работает:
auto c = std::complex<int>{1,1};
auto [r, i] = c + 2;
Аналогично для пользовательского типа:
template <typename X, typename Y>
class NonPOD {
X x;
Y y;
public:
NonPOD(X x, Y y): x{x}, y{y} {}
X getX() const { return x; }
Y getY() const { return y; }
};
namespace std {
template <size_t I, typename Arg, typename ...Args>
class type_alternatives {
public:
using type = typename type_alternatives<I-1, Args...>::type;
};
template <typename Arg, typename ...Args>
class type_alternatives<0, Arg, Args...> {
public:
using type = Arg;
};
template <size_t I, typename X, typename Y>
class tuple_element<I, NonPOD<X, Y>> {
public:
using type = typename type_alternatives<I, X, Y>::type;
};
} // namespace std
template <size_t I, typename X, typename Y>
typename std::tuple_element<I, NonPOD<X, Y>>::type
get(const NonPOD<X, Y>& t) {
if constexpr (I == 0) return t.getX();
else return t.getY();
}
auto test() {
auto sb = NonPOD{1, 2.};
auto [s, b] = sb;
return s+b;
}
В наших собственных типах мы можем определить метод-член get<i>()
вместо отдельного get<i>(obj)
.
Побочное замечание: существует интересное свойство std::complex<T>:
4
Если z — l-значение типа cv complex, то:(4.1)
выражение reinterpret_cast(z) является хорошо сформированным,(4.2)
reinterpret_cast(z)[0] обозначает вещественную часть z, и(4.3)
reinterpret_cast(z)[1] обозначает мнимую часть z.
Это означает, что мы можем просто выполнить кастинг:
auto c = std::complex<int>{1,1};
auto [r, i] = reinterpret_cast<int(&)[2]>(c);
Однако лучше не использовать его с временными параметрами.