迭代班级成员

时间:2018-04-03 13:53:50

标签: c++ c++11 c++17

您好我想知道我是否可以迭代班级成员,所以我不必为每个班级成员writeElement

我很乐意将它放在for循环中,以便循环所有公共成员。

我的代码:

class Student
{
public:
    string name;
    string lastName;
    int age;
    string gender;
    vector<int> grades;

public:
    void read(istream& in)
    {
        readElement(in, name);
        readElement(in, lastName);
        readElement(in, age);
        readElement(in, gender);
        readElement(in, grades);
    }

    void write(ostream& out)
    {
        //add a loop here
        writeElement(out, name);
        writeElement(out, lastName);
        writeElement(out, age);
        writeElement(out, gender);
        writeElement(out, grades);
    }
};

2 个答案:

答案 0 :(得分:3)

没有简单的方法来达到你想要的。为C ++ 20提出的静态反射将使这成为可能。

现在,您有一些(不太好)的选择:

  • 手动写出readElement / writeElement来电。您可以通过提供更高阶函数并将readElement / writeElement作为参数(有点像访问者)来避免重复。

  • 将整个struct定义包装到一个可变参数宏中,该宏会自动为每个数据成员生成一个访问者。

  • 使用std::tuple代替struct,并使用std::apply + variadic generic lambda to&#34; iterate&#34;成员。

  • 如果您的类型支持它,您可以使用magic_get,其中(ab)使用结构化绑定和其他疯狂的元编程技巧来提供有限形式的静态反射。

访客解决方案示例:

template <typename F>
void visit(F&& f)
{
    f(name);
    f(lastName);
    f(age);
    f(gender);
    f(grades);   
}

void read(istream& in)
{
    visit([&in](auto&& x){ readElement(in, x); });
}

void write(ostream& out)
{
    visit([&in](auto&& x){ writeElement(in, x); });
}

元组解决方案示例:

std::tuple
<
    string      /* name */,
    string      /* lastName */,
    int         /* age */,
    string      /* gender */,
    vector<int> /* grades */
> data;

template <typename F>
void visit(F&& f)
{
    std::apply([](auto&&... xs){ (f(xs), ...); }, data);
}

答案 1 :(得分:0)

由于您正在寻找的功能尚不存在;也许这种性质的东西会帮助你。这个目前正在编译和运行,因为我测试了一些基本功能,但我还没有做过任何详尽的测试。我将学生班级中的所有学生信息抽象为一个元组成员,并且有一些重载的operator()s。为了这个工作原样;我不得不超载ostream和amp;向量和元组的istream运算符可以使其工作。

#include <iostream>
#include <string>
#include <utility>
#include <tuple>
#include <vector>

// ostream operator<< for vector<T>
template<class T>
std::ostream& operator<<( std::ostream& out, const std::vector<T>& v ) {
    out << "{ ";
    for( auto& a : v )
        out << a << ' ';
    out << '}';
    return out;
}

// istream operator>> for vector<T>
template<class T>
std::istream& operator>>( std::istream& in, std::vector<T>& v ) {
    int i;
    std::string line;
    std::getline( std::cin, line );
    std::istringstream iss( line );
    while( iss >> i ) {
        v.push_back( i );
    }
    return in;
}

// function templates & ostream operator<< for tuple<T>
template<std::size_t> struct int_ {};

template<class Tuple, size_t Pos>
std::ostream& print_tuple( std::ostream& out, const Tuple& t, int_<Pos> ) {
    out << std::get<std::tuple_size<Tuple>::value - Pos>( t ) << ' ';
    return print_tuple( out, t, int_<Pos - 1>() );
}

template<class Tuple>
std::ostream& print_tuple( std::ostream& out, const Tuple& t, int_<1> ) {
    return out << std::get<std::tuple_size<Tuple>::value - 1>( t );
}

template<class... Args>
std::ostream& operator<<( std::ostream& out, const std::tuple<Args...>& t ) {
    return print_tuple( out, t, int_<sizeof...(Args)>() );
}

// function templates & istream operator << for tuple<T>
template<class Tuple, size_t Pos>
std::istream& write_tuple( std::istream& in, Tuple& t, int_<Pos> ) {
    in >> std::get<std::tuple_size<Tuple>::value - Pos>( t );
    return write_tuple( in, t, int_<Pos - 1>() );
}

template<class Tuple>
std::istream& write_tuple( std::istream& in, Tuple& t, int_<1> ) {
    return in >> std::get<std::tuple_size<Tuple>::value - 1>( t );
}

template<class... Args>
std::istream& operator>>( std::istream& in, std::tuple<Args...>& t ) {
    return write_tuple( in, t, int_<sizeof...(Args)>() );
}
// --------------------------------------------------

// class proto type for friend operators
template<class... T>
class StudentInfo;

template<class... T>
std::ostream& operator<< <>( std::ostream& out, const StudentInfo<T...>& c );

template<class... T>
std::istream& operator>> <>( std::istream& in, StudentInfo<T...>& c );

//template<typename... Args>
template<class...Args>
class StudentInfo {
public
    std::tuple<Args...> members;


    explicit StudentInfo(Args&&... args ) {
        members = std::make_tuple<Args...>( std::move( args )... );
    } 

    const StudentInfo<Args...>& operator() ( Args&&... args ) {
        members = std::make_tuple<Args...>( std::forward<Args>( args )... );
        return *this;
    }

    const StudentInfo<Args...> operator() ( Args&&... args ) const {
        members = std::make_tuple<Args...>( std::forward<Args>( args )... );
        return *this;
    }

    template<Args...>
    friend std::ostream& operator<< <>(std::ostream& out, const StudentInfo<Args...>& c);

    template<Args...>
    friend std::istream& operator>> <>( std::istream& in, StudentInfo<Args...>& c );

    StudentInfo<Args...>& operator=( StudentInfo<Args...>& c ) {
        if ( members == c.members ) 
            return *this;
        members = c.members;
        return *this;
    }

};

template<class... T>
std::ostream& operator<< <>( std::ostream& out, StudentInfo<T...>& c ) {
    return out << c.members;
}

template<class... T>
std::istream& operator>> <>( std::istream& in, StudentInfo<T...>& c ) {
    return in >> c.members;
}

使用它如下:

int main() {
    std::string first{ "Some" };
    std::string last{ "Day" };
    int age = 1000;
    std::string sex{ "Unknown" };
    std::vector<int> grades{ 99, 98, 97, 92, 89, 88 };

    // create student info
    StudentInfo< std::string, std::string, int,
                 std::string, std::vector<int> > 
        studentA( std::move(first), std::move(last), 
                  std::move(age), std::move(sex), 
                  std::move(grades)
        );

    // outstream student info
    std::cout << studentA << '\n';

    // reset temps
    first.clear();
    last.clear();
    age = 0;
    sex.clear();
    grades.clear();

    // create 2nd student & assign new information from user input
    StudentInfo< std::string, std::string, int,
                 std::string, std::vector<int> >
        studentB( std::move(first), std::move(last), 
                  std::move(age), std::move(sex), 
                  std::move(grades)
        );

    // Check to make sure it has empty fields
    std::cout << "Student B's information\n" << studentB << '\n';

    // Now let's enter some stuff from the console and populate studentB
    std::cout << "\nEnter the student's information\n";
    std::cin >> studentB;

    // Let's check studentB's info
    std::cout << studentB << '\n';

    // Another step let's check our assignment operator
    StudentInfo< std::string, std::string, int,
                 std::string, std::vector<int> >
        studentC( std::move( first ), std::move( last ),
                  std::move( age ), std::move( sex ),
                  std::move( grades ) 
        );

    // Check studentC it should be empty
    std::cout << "Student C's information\n" << studentC << '\n';

    // Let's set studentC to studentA;
    studentC = studentA;

    // Print C's info
    std::cout << "Student C's new information\n" << studentC << '\n';

    // Finally test out the operator()
    studentC( std::move( std::get<0>( studentB.members ) ),
              std::move( std::get<1>( studentB.members ) ),
              std::move( std::get<2>( studentB.members ) ),
              std::move( std::get<3>( studentB.members ) ),
              std::move( std::get<4>( studentB.members ) )
    );
    std::cout << studentC << '\n';


    std:cout << "\nPress any key and enter to quit.\n";
    std::cin.get();
    return 0;
}

实现看起来很复杂,但在进行可变参数类模板和使用元组时需要一些部分。

仅仅为元组编写重载和辅助函数是不够的。它适用于所有元素都是基本类型<T>的元组。但是,当元组的成员不是基本类型(如容器std::vector<T>)时。然后代码将中断,它将无法编译,因为它不知道如何处理向量的情况。上面的代码将按原样运行;但是如果你试图替换vector的另一个容器类型,那么元组的重叠将会再次中断,因为它只知道如何处理矢量类型。您必须手动添加其他容器的支持。