非会员转换,不同类型的同一会员

时间:2015-05-22 14:15:36

标签: c++ c++11

我有两个我无法修改的类,两个都有完全相同的成员:

class Pose1 {
    public:
    double x,y;
};
class Pose2 {
   public:
   double x,y;
};

代码的一部分使用Pose1,另一部分使用Pose2。有没有办法将这些隐含地转换成彼此?现在我必须一直写

Pose1 p1(0.5, 0.5);
Pose2 p2(p1.x,p2.y);

我知道我可以写一个转换函数,只执行一次。但是我有很多不同的类型,有很多争论。

我有什么方法可以做类似的事情:

Pose2 p2  = static_cast<Pose2>(p1);

我无法使用成员函数,因为我无法更改此代码。

谢谢!

7 个答案:

答案 0 :(得分:7)

试试这个:

template <class Dest, class Src>
Dest pose_cast(const Src &src)
{
    return Dest(src.x, src.y) ;
}

用法:

Pose1 p1 ;
Pose2 p2 = pose_cast<Pose2, Pose1>(p1)

适用于两次转化。

答案 1 :(得分:2)

不,没有。 Pose1Pose2是不同的类型,没有共同的基础。事实上,他们碰巧拥有相同的成员类型和名称并不重要。如果你想让它们互相转换,你必须明确地提供这种支持。

一种方法是提供外部转换功能:

template <typename Pose, typename T>
Pose toPose(const T& pose) {
    return Pose{pose.x, pose.y};
}

Pose2 pose2 = toPose<Pose2>(pose1);

或者,您可以使用void_t编写一个type_trait,以查看某些内容是否为Pose

template <typename T, typename = void>
struct is_pose : std::false_type { };

template <typename T>
struct is_pose<T, void_t<
               std::enable_if_t<std::is_same<decltype(T::x), double>::value>,
               std::enable_if_t<std::is_same<decltype(T::y), double>::value>>>
: std::true_type { };

然后只为你的所有姿势编写“姿势式”构造函数:

class Pose2 {
    template <typename T, typename = std::enable_if_t<is_pose<T>::value>>
    Pose2(T&& pose_like)
    : x(pose_like.x)
    , y(pose_like.y)
    { }
};

Pose2 pose{pose1}; // works

答案 2 :(得分:0)

简短的回答是否定的。

但是你可以通过编写这个函数重载集并将它们放在一个实用程序头文件中来节省很多工作:

inline Pose2 to_pose2(const Pose1& p1) {
    return Pose2 { p1.x, p1.y };
}

inline Pose2& to_pose2(Pose2& p2) {
    return p2;
}

inline const Pose2& to_pose2(const Pose2& p2) {
    return p2;
}

然后可以这样使用:

void function_that_needs_a_pose2(const Pose2& p2);

Pose1 p1;
Pose2 p2;

function_that_needs_a_pose2(to_pose2(p1));
function_that_needs_a_pose2(to_pose2(p2));

...或者你可以在PoseHolder中保存你的Pose [n]对象,它允许隐式演员:

inline Pose2 to_pose2(const Pose1& p1) {
    return Pose2 { p1.x, p1.y };
}

inline Pose1 to_pose1(const Pose2& p1) {
    return Pose1 { p1.x, p1.y };
}

struct PoseHolder {
    PoseHolder(Pose1 p1) : _pose1 { std::move(p1) } {}
    PoseHolder(Pose2 p2) : _pose2 { std::move(p2) } {}


    operator const Pose1&() const {
        if (!_pose1.is_initialized()) {
            _pose1 = to_pose1(_pose2.value());
        }
        return _pose1.value();
    }

    operator const Pose2&() const {
        if (!_pose2.is_initialized()) {
            _pose2 = to_pose2(_pose1.value());
        }
        return _pose2.value();
    }

private:
    mutable boost::optional<Pose1> _pose1;
    mutable boost::optional<Pose2> _pose2;
};

答案 3 :(得分:0)

不,如果您无法修改这两种类型中的至少一种,则无法进行隐式转换。

有些人可能建议通过引用将一个转换为另一个,但这仍然不是隐含的,所以你不妨编写一个转换函数,而不是没有未定义的行为。 (强制转换违反了严格的别名规则,因此是UB。)

答案 4 :(得分:0)

我会这样做:

class Pose1 {
    public:
    double x,y;
};
/*class Pose2 {
   public:
   double x,y;
};*/

typedef Pose1 Pose2;

我不明白为什么不呢?

答案 5 :(得分:0)

您列出的类的内存布局应该相同。

没有隐式转换,但您可以只转换指针类型。

如果您的某个类开始拥有虚拟表,但所有投注都已关闭,因为它取决于编译器,您需要进行真正的显式转换。

您可能希望将转换包装在一个函数中以使其整洁,如果您以后需要做更多工作,则将其封装起来。

答案 6 :(得分:0)

如果您不想在演员表中指定目的地类型,则为另一种解决方案:

#include <iostream>

class Pose1 {
public:
    double x,y;
};
class Pose2 {
public:
    double x,y;
};

namespace detail {
    template<class T, class U>
    struct PoseCaster {

        operator U
        () const
        {
            return U { _source.x, _source.y };
        }

        operator
        const T&
        () const
        {
            return _source;
        }

        const T& _source;
    };
}

detail::PoseCaster<Pose1, Pose2> pose_cast(const Pose1& pose) {
    return detail::PoseCaster<Pose1, Pose2> { pose };
}

detail::PoseCaster<Pose2, Pose1> pose_cast(const Pose2& pose) {
    return detail::PoseCaster<Pose2, Pose1> { pose };
}

void funca(const Pose1&) {
    std::cout << "funca" << std::endl;
}
void funcb(const Pose2&) {
    std::cout << "funcb" << std::endl;
}

int main()
{
    Pose1 a;
    Pose2 b;

    funca(pose_cast(a));
    funca(pose_cast(b));
    funcb(pose_cast(a));
    funcb(pose_cast(b));
}