Преамбула : Эй, скажем, у меня есть различные представления данных, которые я хочу беспрепятственно преобразовывать между ними любым способом. Представления вне моего контроля. Мой практический пример - объектная ориентация в 3D: у нас есть кватернионы, углы Эйлера, углы-оси и матрицы вращения, все в разных классах из разных библиотек (которые я должен использовать). Для этого я создал прокси-класс, который хранит значение в одном конкретном представлении и может преобразовать в него с помощью перегруженных конструкторов , а из - с помощью перегруженных операторов преобразования типов , например:
Eigen::Quaterniond eig_quaternion = AttitudeConvertor(roll, pitch, yaw);
tf2::Quaternion tf2_quaternion = AttitudeConvertor(eig_quaternion);
Проблема : Пока все хорошо, пока я не захочу перегрузить преобразование типов в std :: tuple , что удобно, например, при возврате yaw , pitch и roll angles, которые будут выглядеть следующим образом:
auto [roll2, pitch2, yaw2] = AttitudeConvertor(tf2_quaternion);
Класс может быть скомпилирован, но присваивания для auto [a, b, c]
и std::tie(a, b, c)
не работают. Обходной путь может быть выполнен в виде специальной функции, которая возвращает кортеж. Или путем создания пользовательского класса только для хранения трех двойных. Они работают просто отлично, но это уже не так просто.
Я знаю, что функции не могут быть перегружены типом возвращаемого значения. Вот почему я создал этот прокси-класс. Но есть ли другой способ вернуть кортеж? Даже если это только для одного варианта кортежа? Или я должен подойти к этой проблеме иначе?
Я подготовил минимальный (не) рабочий пример в теме более простого преобразования чисел :
#include <iostream>
#include <math.h>
#include <tuple>
using namespace std;
class NumberConvertor {
public:
// | ---------------------- constructors ---------------------- |
NumberConvertor(const int& in) {
value_ = double(in);
}
NumberConvertor(const double& in) : value_(in){};
// | ------------------- typecast operators ------------------- |
operator int() const {
return int(value_);
}
operator double() const {
return value_;
}
// return the integer and the fractional part
operator std::tuple<int, double>() const {
int int_part = floor(value_);
double frac_part = fmod(value_, int_part);
return std::tuple(int_part, frac_part);
}
// | ------------------------ functions ----------------------- |
// the workaround
std::tuple<int, double> getIntFrac(void) const {
int int_part = floor(value_);
double frac_part = fmod(value_, int_part);
return std::tuple(int_part, frac_part);
}
private:
double value_; // the internally stored value in the 'universal representation'
};
int main(int argc, char** argv) {
// this works just fine
int intval = NumberConvertor(3.14);
double fracval = NumberConvertor(intval);
cout << "intval: " << intval << ", fracval: " << fracval << endl;
// this does not compile
// auto [int_part, frac_part] = NumberConvertor(3.14);
// neither does this
// int a;
// double b;
// std::tie(a, b) = NumberConvertor(3.14);
// the workaround
auto [int_part2, frac_part2] = NumberConvertor(3.14).getIntFrac();
cout << "decimal and fractional parts: " << int_part2 << ", " << frac_part2 << endl;
std::tie(int_part2, frac_part2) = NumberConvertor(1.618).getIntFrac();
cout << "decimal and fractional parts: " << int_part2 << ", " << frac_part2 << endl;
return 0;
};
Makefile:
main: main.cpp
g++ -std=c++17 main.cpp -o main
all: main
Ожидаемый результат:
intval: 3, fracval: 3
decimal and fractional parts: 3, 0.14
decimal and fractional parts: 1, 0.618