graywolf
graywolf

Reputation: 7490

Using member declaration with enable_if?

I need conditional using member declaration.

template <bool> struct B;
template <> struct B<true> { void foo(); };
template <> struct B<false> { };

template <typename T>
struct A : public B<is_default_constructible<T>::value> {
    using B<is_default_constructible<T>::value>::foo();

    void foo(int) {}
};

This obviously doesn't work, because B<bool>::foo is not defined in half the cases. How can I achieve that? To have B<>::foo() visible in A<T> scope beside foo(int)?

Upvotes: 5

Views: 769

Answers (2)

Not a real meerkat
Not a real meerkat

Reputation: 5729

Use specialization.

enable_if can't be used for that. You need to specialize struct A too.

#include <type_traits>

template <bool> struct B;
template <> struct B<true> { void foo(); };
template <> struct B<false> { };

template <typename T, bool default_constructible = std::is_default_constructible<T>::value>
struct A : public B<default_constructible> {
    using B<default_constructible>::foo;

    void foo(int) {}
};

template<typename T>
struct A<T, false> : public B<false> {
    void foo(int) {}
};

Avoiding duplicate code for foo(int)

If foo(int) will have the same functionality in both cases, you may want to derive it from another base struct:

#include <type_traits>

template <bool> struct B;
template <> struct B<true> { void foo(); };
template <> struct B<false> { };

template<typename T>
struct C {
  void foo(int) {}
};

template <typename T, bool default_constructible = std::is_default_constructible<T>::value>
struct A : public B<default_constructible>, public C<T> {
    using B<default_constructible>::foo;
    using C<T>::foo;
};

template<typename T>
struct A<T, false> : public B<false>, public C<T> {
    using C<T>::foo;
};

Removing that ugly bool

Finally, to remove that bool from struct A's template parameters, you may want to forward the responsibility of selecting the overloads of foo to a base class. This also has the advantage of not duplicating code for other struct A's members you may want to add.

#include <type_traits>

template <bool> struct B;
template <> struct B<true> { void foo(); };
template <> struct B<false> { };

template<typename T>
struct C {
  void foo(int) {}
};

template <typename T, bool default_constructible = std::is_default_constructible<T>::value>
struct base_A : public B<default_constructible>, public C<T> {
    using B<default_constructible>::foo;
    using C<T>::foo;
};

template<typename T>
struct base_A<T, false> : public B<false>, public C<T> {
    using C<T>::foo;
};

template <typename T>
struct A : public base_A<T> {
    // Other members.
};

Upvotes: 0

Mohamad Elghawi
Mohamad Elghawi

Reputation: 2124

This is my solution. I'm sure it's won't be the best but it gets the job done.

struct A {
    void foo(int) {}
};

struct A should contain methods you want defined in both cases.

template <bool> struct B;
template <> struct B<false> : A {};
template <> struct B<true> : A { 
    using A::foo;
    void foo() {} 

};

In case of B<false>, only void foo(int) is defined. In case of B<true>, both void foo(int) and void foo() are defined.

template <typename T>
struct C : public B<is_default_constructible<T>::value> {};

Now I don't have to worry about B<is_default_constructible<T>::value>::foo() not being defined in certain cases.

class D { D() = delete; };

int main()
{
    C<int> c1;
    c1.foo(1234);
    c1.foo();
    // both methods are defined for C<int>

    C<D> c2;
    c2.foo(1234);
    // c2.foo(); // undefined method

    return 0;
}

Upvotes: 0

Related Questions