hyde
hyde

Reputation: 62797

Accessing struct member in a template function, when the struct has that member

I'd like to implement this functionality with C++ templates as nicely as possible.

struct Foo {
    int x = 0;
    int y = 0;
};

struct Bar {
    int x = 1;
};

// I want to call this function for both Foo and Bar instances
template <typename T>
int helperFunc(const T &item) {
    // many lines of code I don't want to repeat
    int result = item.x;
    result += item.x;
    result *= item.x; 

    // only do code below if item has y
    result += item.y;

    return result;
}

Constraints: I can not alter Foo or Bar, so I can't for example add abstract interface superclass for Foo. I don't want to specialize the function specifically for any named class either, and I don't want to repeat any logic code.

My current, rather dumb solution is to have template <typename T> int helperFunc(const T &item, int y = 0) {...};, and then for a Foo call it like helperFunct(aFoo, aFoo.y), which works for my use case, but is not very nice. I'd like to put the decision to use y inside the template. I tried to find something applicable from here, but couldn't really come up with a way to use any of that to achieve the above.

Upvotes: 1

Views: 240

Answers (2)

songyuanyao
songyuanyao

Reputation: 172924

You can make type trait to check whether the type has the member y or not.

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

then you can apply constexpr if (since C++17).

if constexpr (has_y<T>::value) // only do code below if item has y
    result += item.y;

LIVE

Upvotes: 2

Erlkoenig
Erlkoenig

Reputation: 2754

You can use SFINAE on function overloads to check for the existence of y, then use if-constexpr:

template <typename T>
std::true_type hasY_ (decltype(std::declval<T> ().y)*);

template <typename T>
std::false_type hasY_ (...);

template <typename T>
static constexpr bool hasY = decltype(hasY_<T>(0))::value;

// I want to call this function for both Foo and Bar instances
template <typename T>
int helperFunc(const T &item) {
    // many lines of code I don't want to repeat
    int result = item.x;
    result += item.x;
    result *= item.x; 

    // only do code below if item has y
    if constexpr (hasY<T>)
        result += item.y;

    return result;
}

Upvotes: 4

Related Questions