我的代码中有几个结构都有
std::pair
或cv::Point2d
示例:
struct CartesianCoordinates {
std::pair<double, double> xy;
void x(double val) { xy.first = val; }
void y(double val) { xy.second = val; }
double x() const { return xy.first; }
double y() const { return xy.second; }
};
struct GeographicCoordinates {
std::pair<double, double> xy;
void longtitude(double val) { xy.first = val; }
void lattitude(double val) { xy.second = val; }
double longtitude() const { return xy.first; }
double lattitude() const { return xy.second; }
};
现在我希望能够在隐式之间进行转换,如下所示:
CartesianCoordinates returnCartesian()
{
CartesianCoordinates c;
c.x(5);
c.y(-13);
return c;
}
void getGeographicCoordinates(const GeographicCoordinates& c) {
std::cout << c.longtitude() << '\n';
}
int main()
{
getGeographicCoordinates(returnCartesian());
}
如果没有
,有没有办法实现这一目标实际上reinterpret_cast
/指针应该可以工作,但是存在我不知道的任何现代C ++机制,这可以帮助我用最少的结构实现开销来解决它,只是使用的事实所有结构都具有相同的结构?
答案 0 :(得分:4)
好吧,只要你不想转换数据成员中的数据,并且只要成员总是命名相同的东西,就可以使用模板转换运算符。然后使用SFINAE将模板限制为仅具有与该类的指定成员类型相同的命名成员的类型。添加
template <typename T, std::enable_if_t<std::is_same_v<std::pair<double, double>, decltype(std::declval<T&>().xy)>>* = nullptr>
operator T() { return T{xy}; }
两个类允许它们相互转换并允许
int main()
{
getGeographicCoordinates(returnCartesian());
}
运行(live example)
如果这些类不使用相同的成员变量名,那么你可以让它们全部表面一个名为相同的typedef,它是数据成员的类型并使用它。那看起来像是
template <typename T, std::enable_if_t<std::is_same_v<my_variable_typedef, typename T::my_variable_typedef>>* = nullptr>
operator T() { return T{xy}; }
答案 1 :(得分:0)
这可以通过定义成员的访问接口来解决。
我建议您重复使用get
惯用法,并为希望此行为处理的所有结构定义模板化(在索引上)get
自由函数。
然后,您必须以与模板类似的方式定义应对所有这些类型起作用的函数,并通过get
接口访问成员。
答案 2 :(得分:0)
不,没有大量的结构模板。
实际上reinterpret_cast / pointers应该可以工作
实际上并非如此。您可以使用对象是标准布局结构中的第一个元素的事实从struct到pair重新解释,但是如果该结构类型实际存在,则只能从pair重新解释为struct 。
如果结构类型的对象不在那里,则从对到结构的重新解释是未定义的行为。
这是尝试在c++17中使用最少的样板文件执行此操作,同时保持通用。我们从一个空的CRTP空父类继承,以注入一些朋友和转换运算符。此外,我们必须添加一些代码,将每个成员公开给CRTP父级。
template<class A, class B>
struct tie_same;
template<class A, class B>
using tie_check = typename tie_same<A,B>::type;
template<class D>
struct structured {
friend auto as_tie( structured<D>& s ) {
auto& self = static_cast<D&>(s);
auto members = get_members(self);
return std::apply( [&self](auto...pm) {
return std::tie( (self.*pm)... );
}, members );
}
friend auto as_tie( structured<D> const& s ) {
auto& self = static_cast<D const&>(s);
auto members = get_members(self);
return std::apply( [&self](auto...pm) {
return std::tie( (self.*pm)... );
}, members );
}
template<class T,
std::enable_if_t<
tie_check<T,D>{},
bool
> = true
>
operator T() const& {
T tmp;
as_tie(tmp) = as_tie( *this );
return tmp;
}
};
template<class A, class B>
struct tie_compatible {
template<class X>
using tie_type = decltype( as_tie( std::declval<X>() ) );
using type = std::is_same< tie_type<A&>, tie_type<B&> >;
};
有点棘手,但能完成这项工作。
要在X
类型中支持此系统,您需要:
X
继承structured<X>
。auto members(X const& x)
,返回指向您关注的指针成员的元组。以下是使用此功能增强的类型:
struct CartesianCoordinates: structured<CartesianCoordinates> {
std::pair<double, double> xy;
friend auto get_members( CartesianCoordinates const& self ) {
return std::make_tuple( &CartesianCoordinates::xy );
}
void x(double val) { xy.first = val; }
void y(double val) { xy.second = val; }
double x() const { return xy.first; }
double y() const { return xy.second; }
};
struct GeographicCoordinates: structured<GeographicCoordinates> {
std::pair<double, double> xy;
friend auto get_members( GeographicCoordinates const& self ) {
return std::make_tuple( &GeographicCoordinates::xy );
}
void longtitude(double val) { xy.first = val; }
void lattitude(double val) { xy.second = val; }
double longtitude() const { return xy.first; }
double lattitude() const { return xy.second; }
};
在c++14中,只需实施您自己的notstd::apply
。
在c++11中,由于缺少退货类型扣除而令人讨厌。
#define RETURNS(...) \
noexecpt(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
RETURNS
可以稍微改变这种痛苦。
auto foo()
RETURNS( 1+2 )
替换
auto foo() {
return 1+2;
}
有时会奏效。 (仍有重要的差异,但你能做些什么)