Как передать и проверить много параметров функции?
Обернуть всё в класс. С методом проверки. Все остальные наследуют от него/друг от друга.
Дополню камрада ManWithBear 
Не нужно структуру отдельно переделывать в класс. Это и так класс, только доступ по-умолчанию public. И метод проверки можно не добавлять, а сделать внешним по отношению к структуре. Свободные функции не всегда плохо. Плюсы такого подхода в том, что memory-layout будет предсказуемый, а если в структуре нет non-POD типов то они останутся POD типами.
Т.е. получится что-то вроде такого:#include <iostream>
#include <iomanip>
using namespace std;

struct Point
{
  int x;
  int y;
};
inline bool is_valid(const Point& p)
{
  bool result = true;
  // some checks
  return result;
}

struct Rect : Point
{
  int width;
  int height;
};
inline bool is_valid(const Rect& r)
{
  bool result = is_valid(static_cast<const Point&>(r)) && r.width > 0 && r.height > 0;
  return result;
}

int main() {
	// теряется возможность делать так:
	//Rect r = {0, 1, 2, 3};
	Rect r{};
	r.x = 0;
	r.y = 1;
	r.width  = 10;
	r.height = 11;
	
	auto check = is_valid(r);
	
	cout << " x=" << r.x
	     << " y=" << r.y
	     << " w=" << r.width
	     << " h=" << r.height
	     << " valid=" << check
	     << endl;
	
	cout << sizeof(Rect) << "/" << sizeof(int)*4 << endl;
	
	uint8_t *ptr = reinterpret_cast<uint8_t*>(&r);
	auto beg = ptr;
	auto end = ptr + sizeof(Rect);
	for (auto it = beg; it != end; ++it) {
		cout << hex << setfill('0') << setw(2) << (int)*it << " ";
	}
	cout << endl;
	
	return 0;
}
Можно функцию проверки сделать и методом класса/структуры и, при этом, не виртуальной. Тогда проверка будет вызываться только в соответствии с типом. Тогда можно обойтись без static_cast, получится что-то вроде:#include <iostream>
#include <iomanip>
using namespace std;

struct Point
{
  int x;
  int y;
  
  bool is_valid() const
  {
  	bool result = true;
  	// some checks
  	return result;
  }
};

struct Rect : Point
{
  int width;
  int height;

  bool is_valid() const
  {
  	bool result = Point::is_valid() && width > 0 && height > 0;
  	return result;
  }
};

void pass_point(const Point &p)
{
	cout << "point is valid: " << p.is_valid() << endl;
}

int main() {
	// теряется возможность делать так:
	//Rect r = {0, 1, 2, 3};
	Rect r{};
	r.x = 0;
	r.y = 1;
	r.width  = 0; // make invalid
	r.height = 11;
	
	auto check = r.is_valid();
	
	cout << " x=" << r.x
	     << " y=" << r.y
	     << " w=" << r.width
	     << " h=" << r.height
	     << " valid=" << check
	     << endl;
    
    // но следующий вызов скажет что точка валидна:
    // вызов валиден, т.к. Rect отнаследован от Point
    pass_point(r);
	
	cout << sizeof(Rect) << "/" << sizeof(int)*4 << endl;
	
	uint8_t *ptr = reinterpret_cast<uint8_t*>(&r);
	auto beg = ptr;
	auto end = ptr + sizeof(Rect);
	for (auto it = beg; it != end; ++it) {
		cout << hex << setfill('0') << setw(2) << (int)*it << " ";
	}
	cout << endl;
	
	return 0;
}
Сложности могут возникнуть, когда появятся ромбические связи и потребуется виртуальное наследование.