Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to overload a member function based on pointer vs non-pointer type

Tags:

c++

Given the following situation:

template <typename T>
class Whatever
{
public:
    Whatever(T &&t): _t(std::move(t)) { }

private:
    T _t;
};

When T is a pointer type, I need to check the constructor's t arg to see if it's -1 (don't ask), and change it to a nullptr before assigning it to _t. In other words, I need to overload this constructor for pointer types.

Does anyone know if this is doable?

Note: Even I partial specialize the class on pointer types, I'd want that class to inherit from the above class itself if possible (since the behaviour of both classes is identical except for this), but don't know if that's possible. Any help would be appreciated. Thanks.

like image 956
mikestz Avatar asked Feb 10 '23 00:02

mikestz


2 Answers

You can utilize tag-dispatching and delegating constructors:

#include <type_traits>

template <typename T>
class Whatever
{
public:
    Whatever(T&& t)
        : Whatever(std::move(t), std::is_pointer<T>{})
    {
    }

private:
    Whatever(T&& t, std::true_type)
        : _t(/*initialize _t as a pointer*/)
    {
    }

    Whatever(T&& t, std::false_type)
        : _t(/*initialize _t as a non-pointer*/)
    {
    }

    T _t;
};
like image 80
Piotr Skotnicki Avatar answered Feb 11 '23 13:02

Piotr Skotnicki


You could faff about using std::enable_if<...> and deal with overload sets. It is much easier to not specialize the constructor and just do the appropriate transformation differently:

template <typename T>
class WhateverBase {
protected:
    T _t;
    WhateverBase(T&& t): _t(std::move(t)) {}
};
template <typename T>
class WhateverBase<T*> {
protected:
    T* _t;
    WhateverBase(T* t): _t(adjust(t)) {}
};
template <typename T>
class Whatever: WhateverBase<T>
{
public:
    Whatever(T&& t): WhateverBase(std::move(t)) {}
    // ...
};

The above logic assumes there is additional code in Whatever which is shared between the versions using different types of T and which shouldn't be replicated.

Alternatively it would be possible to create a custom version of move() which does the logic:

template <typename T>
std::remove_reference_t<T>&& adjust_move(T&& ref) {
    return static_cast<std::remove_reference_t<T>&&>(ref);
}
template <typename T>
T* adjust_move(T* ref) { return adjust(ref); }

Whatever<T>::Whatever(T&& t): _t(adjust_move(t)) {}

(actually, this approach is simpler...).

... and use this in place of std::move(t) in the constructor.

like image 28
Dietmar Kühl Avatar answered Feb 11 '23 12:02

Dietmar Kühl