2

What I want is

template<typename T>
bool larger(T a, T b){
    if(a.x>b.x&&a.y>b.y&&a.z>b.z) return true;
    return false;
}

. But , each T may only have some of the x,y,z values, for example

struct Txy{
    double x,y;
}

So for Txy I would only judge by x and y. I know I could specify the template to do so, but with many Ts there's no difference between write a function for each T...

Is there any better way to deal with this problem?

Bobi.Liu
  • 311
  • 1
  • 8
  • I don't have a good template answer, but what about replacing the variables with an array looping? Normally I'd have a `operator>` override for each class. – user4581301 Sep 11 '20 at 03:55
  • @user4581301 the bad thing is that I can't change those structs, and I guess array would cause more problems like 'which of the array is x , and which is y' – Bobi.Liu Sep 11 '20 at 04:02
  • @JaMiT ah, i should fix it – Bobi.Liu Sep 11 '20 at 04:03
  • @Bobi.Liu See [check if member exists using enable_if](https://stackoverflow.com/questions/13786888/check-if-member-exists-using-enable-if) (the answers, not just the title). – dxiv Sep 11 '20 at 04:10
  • @user4581301 I'd advise against naming this `operator>`, because it isn't a [Compare](https://en.cppreference.com/w/cpp/named_req/Compare) – Caleth Sep 11 '20 at 08:41

2 Answers2

0

You say you can not modify the structs... but are they raw structs or do they have constructors?

If they are raw structs, what you can do is to use the numero uno solution for everything in computers: add one extra layer of indirection.

Instead of having larger compare the Ts directly and have to overload or specialize each one, have a new larger2 that compares "completed" versions of the structs, where the completed version is computed more or less generically:


// Your original one, won't work
template<typename T>
bool larger(T a, T b){
    if(a.x>b.x&&a.y>b.y&&a.z>b.z) return true;
    return false;
}


struct Txy {
    int x, y;
};

struct Txz {
    int x, z;
};

struct Txyz {
    int x, y, z;
};

// This struct "completizes" Ts, adding manually the missing members.
// Note for this to work, we are forced to add constructors to Completizer
// (unless I am missing something)
template <typename T> 
struct Completizer: T {
    // this constructor is needed
    Completizer (T const& t): T(t) {}
};

template<> struct Completizer<Txy>: Txy {
    int z;
    Completizer (Txy const& t): Txy(t), z(0) {}
};

template<> struct Completizer<Txz>: Txz {
    int y;
    Completizer (Txz const& t): Txz(t), y(0) {}
};

// this is the thing that actually does the magic

template<typename T>
bool larger2_impl(Completizer<T> const& a, Completizer<T> const& b) {
    if(a.x>b.x&&a.y>b.y&&a.z>b.z) return true;
    return false;
}

// the trick here is to have our "larger" function not compare the Ts directly, 
// but do so by means of Completizer<T>

template<typename T>
bool larger2(T const& a, T const& b) {
    return larger2_impl(Completizer<T>(a), Completizer<T>(b));
}

int main () {
    Txy xy1 = {1, 2}, xy2 = {2, 3};
    Txz xz1 = {1, 2}, xz2 = {2, 3};
    Txyz xyz1 = {1, 2, 3}, xyz2 = {2, 3, 4};
    // only here to avoid warnings
    (void)xy1; (void)xy2;
    (void)xz1; (void)xz2;
    
    //larger(xy1, xy2); // does not compile
    larger2(xyz1, xyz2); // compiles
    larger2(xy1, xy2); // compiles
    larger2(xz1, xz2); // compiles
}

The advantage of this method is that it scales somewhat easier if you start adding more members, since you can derive Completizer<Txyzw> from Completizer<Txyz>.

Luis Machuca
  • 1,047
  • 9
  • 16
-1
#include <iostream>

template <typename T, typename = void> struct has_member_x : std::false_type{};
template <typename T> struct has_member_x<T, decltype((void)T::x, void())> : std::true_type {};

template <typename T, typename = void> struct has_member_y : std::false_type{};
template <typename T> struct has_member_y<T, decltype((void)T::y, void())> : std::true_type {};

template <typename T, typename = void> struct has_member_z : std::false_type{};
template <typename T> struct has_member_z<T, decltype((void)T::z, void())> : std::true_type {};

struct Txy {
  int x, y;
};

struct Txz {
  int x, z;
};

template<typename T>
bool larger(T a, T b){
  if constexpr (has_member_x<T>::value)
    if (a.x <= b.x)
      return false;
  if constexpr (has_member_y<T>::value)
    if (a.y <= b.y)
      return false;
  if constexpr (has_member_z<T>::value)
    if (a.z <= b.z)
      return false;
  return true;
}

int main() {
  std:: cout << larger(Txy{1,2}, Txy{2,3}) << "\n";
  std:: cout << larger(Txz{1,2}, Txz{2,3}) << "\n";
}

https://repl.it/repls/LastPoliteType#main.cpp

Bill Lynch
  • 80,138
  • 16
  • 128
  • 173