如何在C++中自动生成操作符?

如何在C++中自动生成操作符?,c++,templates,struct,operator-keyword,C++,Templates,Struct,Operator Keyword,我有以下模板结构: template<class T> struct S { T a,b,c,d,e,f,g,h; }; 编译器无法为两种不同类型隐式生成赋值运算符。您需要明确地定义它。比如说 template<class T> struct S { T a,b,c,d,e,f,g,h; template <class U> S<T> & operator =( const S<U> &

我有以下模板结构:

 template<class T> struct S {
    T a,b,c,d,e,f,g,h;
 };

编译器无法为两种不同类型隐式生成赋值运算符。您需要明确地定义它。比如说

template<class T> struct S 
{
    T a,b,c,d,e,f,g,h;

    template <class U>
    S<T> & operator =( const S<U> &s )
    {
        a = T( s.a );
        b = T( s.b );
        c = T( s.c );
        d = T( s.d );
        e = T( s.e );
        f = T( s.f );
        g = T( s.g );
        h = T( s.h );

        return *this;
    }
};

编译器无法为两种不同类型隐式生成赋值运算符。您需要明确地定义它。比如说

template<class T> struct S 
{
    T a,b,c,d,e,f,g,h;

    template <class U>
    S<T> & operator =( const S<U> &s )
    {
        a = T( s.a );
        b = T( s.b );
        c = T( s.c );
        d = T( s.d );
        e = T( s.e );
        f = T( s.f );
        g = T( s.g );
        h = T( s.h );

        return *this;
    }
};

首先,我建议再看看将这些成员分组是否更有意义。例如,数组可能有意义:

std::array<T, 8> data;
在这种情况下,只要您能够对成员进行分组,问题就会减少

如果这不是一条好的途径,恐怕在语言中出现静态反射之前,最好的方法就是使用以前称为magic_get的方法,它试图通过一些巧妙的、粗糙的、粘贴拷贝的结构化绑定尝试来模拟静态反射。使用C++17:

template<typename OtherT>
S<T>& operator=(const S<OtherT>& other) {
    auto this_tuple = boost::pfr::structure_tie(*this);
    auto other_tuple = boost::pfr::structure_tie(other);
    this_tuple = other_tuple;

    return *this;
}
请特别注意,该类必须可用于结构化绑定


除此之外,您最好的选择可能是将其全部写出来,或者使用一些更强大的基于宏的技术,允许您声明或修改类,使其成为可反射的。例如,Boost.Hana和Boost.Fusion都提供这样的功能。折衷的办法是,代码要简单得多,尤其是如果您只需要这一项功能。

首先,我建议您再看看将这些成员分组是否更有意义。例如,数组可能有意义:

std::array<T, 8> data;
在这种情况下,只要您能够对成员进行分组,问题就会减少

如果这不是一条好的途径,恐怕在语言中出现静态反射之前,最好的方法就是使用以前称为magic_get的方法,它试图通过一些巧妙的、粗糙的、粘贴拷贝的结构化绑定尝试来模拟静态反射。使用C++17:

template<typename OtherT>
S<T>& operator=(const S<OtherT>& other) {
    auto this_tuple = boost::pfr::structure_tie(*this);
    auto other_tuple = boost::pfr::structure_tie(other);
    this_tuple = other_tuple;

    return *this;
}
请特别注意,该类必须可用于结构化绑定


除此之外,您最好的选择可能是将其全部写出来,或者使用一些更强大的基于宏的技术,允许您声明或修改类,使其成为可反射的。例如,Boost.Hana和Boost.Fusion都提供这样的功能。折衷的办法是,代码要简单得多,特别是如果您只需要这一个特性。

正如其他答案所表明的那样,通过大量的boost元编程可以实现您想要的行为。使用以下问题的答案: , 我拼凑了一些东西,迭代两个容器的所有可反射成员,并在铸造后分配它们

然而,我不知道这种方法有多健壮,可能会建议不要在生产代码中使用它

#include <boost/preprocessor.hpp>
#include <boost/type_traits.hpp>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/bind.hpp>

#define REM(...) __VA_ARGS__
#define EAT(...)

#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
#define STRIP(x) EAT x
#define PAIR(x) REM x

template<class M, class T>
struct make_const
{
    typedef T type;
};

template<class M, class T>
struct make_const<const M, T>
{
    typedef typename boost::add_const<T>::type type;
};

#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
    Self & self; \
    field_data(Self & self) : self(self) {} \
    \
    typename make_const<Self, TYPEOF(x)>::type & get() \
    { \
        return self.STRIP(x); \
    }\
    typename boost::add_const<TYPEOF(x)>::type & get() const \
    { \
        return self.STRIP(x); \
    }\
    const char * name() const \
    {\
        return BOOST_PP_STRINGIZE(STRIP(x)); \
    } \
}; \

struct reflector
{
    //Get field_data at index N
    template<int N, class T>
    static typename T::template field_data<N, T> get_field_data(T& x)
    {
        return typename T::template field_data<N, T>(x);
    }

    // Get the number of fields
    template<class T>
    struct fields
    {
        static const int n = T::fields_n;
    };
};

// Custom code for struct assignment below here
struct dual_field_visitor
{
    template<class CA, class CB, class Visitor, class T>
    void operator()(CA & ca, CB & cb, Visitor v, T)
    {
        v(reflector::get_field_data<T::value>(ca), reflector::get_field_data<T::value>(cb));
    }
};

template<class CA, class CB, class Visitor>
void dual_visit_each(CA & ca, CB & cb, Visitor v)
{
    typedef boost::mpl::range_c<int,0,reflector::fields<CA>::n> range;
    boost::mpl::for_each<range>(boost::bind<void>(dual_field_visitor(), boost::ref(ca), boost::ref(cb), v, _1));
}

struct assign_visitor
{
    template<class FieldDataA, class FieldDataB>
    void operator()(FieldDataA fa, FieldDataB fb)
    {
        fa.get() = static_cast<std::decay_t<decltype(fa.get())>>(fb.get());
    }
};

template<typename T>
struct Container
{
    REFLECTABLE
    (
        (T) a,
        (T) b
    )

    template<typename U>
    Container & operator=(const Container<U> & c)
    {
        dual_visit_each(*this, c, assign_visitor());
        return *this;
    }

    Container & operator=(const Container & c)
    {
        if(this != & c)
        {
            dual_visit_each(*this, c, assign_visitor());
        }
        return *this;
    }
};

int main()
{
    Container<int> a {1, 2};
    Container<float> b {3.0f, 4.0f};
    Container<float> c {5.0f, 6.0f};

    a = b;
    b = c;

    return 0;
}

正如其他答案所表明的,通过大量的boost元编程可以实现您想要的行为。使用以下问题的答案: , 我拼凑了一些东西,迭代两个容器的所有可反射成员,并在铸造后分配它们

然而,我不知道这种方法有多健壮,可能会建议不要在生产代码中使用它

#include <boost/preprocessor.hpp>
#include <boost/type_traits.hpp>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/bind.hpp>

#define REM(...) __VA_ARGS__
#define EAT(...)

#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
#define STRIP(x) EAT x
#define PAIR(x) REM x

template<class M, class T>
struct make_const
{
    typedef T type;
};

template<class M, class T>
struct make_const<const M, T>
{
    typedef typename boost::add_const<T>::type type;
};

#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
    Self & self; \
    field_data(Self & self) : self(self) {} \
    \
    typename make_const<Self, TYPEOF(x)>::type & get() \
    { \
        return self.STRIP(x); \
    }\
    typename boost::add_const<TYPEOF(x)>::type & get() const \
    { \
        return self.STRIP(x); \
    }\
    const char * name() const \
    {\
        return BOOST_PP_STRINGIZE(STRIP(x)); \
    } \
}; \

struct reflector
{
    //Get field_data at index N
    template<int N, class T>
    static typename T::template field_data<N, T> get_field_data(T& x)
    {
        return typename T::template field_data<N, T>(x);
    }

    // Get the number of fields
    template<class T>
    struct fields
    {
        static const int n = T::fields_n;
    };
};

// Custom code for struct assignment below here
struct dual_field_visitor
{
    template<class CA, class CB, class Visitor, class T>
    void operator()(CA & ca, CB & cb, Visitor v, T)
    {
        v(reflector::get_field_data<T::value>(ca), reflector::get_field_data<T::value>(cb));
    }
};

template<class CA, class CB, class Visitor>
void dual_visit_each(CA & ca, CB & cb, Visitor v)
{
    typedef boost::mpl::range_c<int,0,reflector::fields<CA>::n> range;
    boost::mpl::for_each<range>(boost::bind<void>(dual_field_visitor(), boost::ref(ca), boost::ref(cb), v, _1));
}

struct assign_visitor
{
    template<class FieldDataA, class FieldDataB>
    void operator()(FieldDataA fa, FieldDataB fb)
    {
        fa.get() = static_cast<std::decay_t<decltype(fa.get())>>(fb.get());
    }
};

template<typename T>
struct Container
{
    REFLECTABLE
    (
        (T) a,
        (T) b
    )

    template<typename U>
    Container & operator=(const Container<U> & c)
    {
        dual_visit_each(*this, c, assign_visitor());
        return *this;
    }

    Container & operator=(const Container & c)
    {
        if(this != & c)
        {
            dual_visit_each(*this, c, assign_visitor());
        }
        return *this;
    }
};

int main()
{
    Container<int> a {1, 2};
    Container<float> b {3.0f, 4.0f};
    Container<float> c {5.0f, 6.0f};

    a = b;
    b = c;

    return 0;
}

没办法。您应该将每个S作为单独的类型来阅读。必须编写两种不同类型的赋值运算符。我想你能做的最好的事情就是设置一个宏来处理代码的自动创建。你的另一个例子是苹果对桔子的比较。在这个例子中,它是double和float之间的简单转换,不管您是否使用模板化别名拼写它。在你的主要问题中,你需要额外的规则,关于编译器何时会生成这样一个赋值运算符,以及它需要处理的任何特殊情况。没有办法。您应该将每个S作为单独的类型来阅读。必须编写两种不同类型的赋值运算符。我想你能做的最好的事情就是设置一个宏来处理代码的自动创建。你的另一个例子是苹果对桔子的比较。在这个例子中,它是double和float之间的简单转换,不管您是否使用模板化别名拼写它。在你的主要问题中,你需要额外的规则来说明编译器何时会生成这样一个赋值运算符以及它需要处理的任何特殊情况。请不要忘记在赋值之前至少进行静态\u转换,并检查类型是否平凡/可正确复制。@QuantumPhysician我为什么要进行静态\u转换?例如,我认为如果我将double赋值给int,最好有一个警告。静态_cast将使此警告静音。@AntonSukhinov我使用cast允许在转换构造函数显式时使用赋值运算符。@AntonSukhinov如果您同意使用警告,那么为什么首先允许使用它?您要么希望转换发生,要么
或者您不希望它发生,因此您触发一个静态断言并阻止编译继续。在程序中保持警告浮动不是一个好主意。请不要忘记在赋值之前至少进行静态\u强制转换,并检查类型是否平凡/可正确复制。@量子物理学家为什么要进行静态\u强制转换?例如,我认为如果我将double赋值给int,最好有一个警告。静态_cast将使此警告静音。@AntonSukhinov我使用cast允许在转换构造函数显式时使用赋值运算符。@AntonSukhinov如果您同意使用警告,那么为什么首先允许使用它?您要么希望转换发生,因此您需要静态_强制转换,要么不希望转换发生,因此您需要触发静态_断言并阻止编译继续。在程序中保持警告浮动不是一个好主意。