Search code examples
c++templatesc++14variadic-templatestemplate-meta-programming

Using declaration on variadic args. How to choose the correct function?


I have a policy based design where I have some function foo() defined in some policies but not others. The base class always inherits from some class called AlwaysBase which has an inferior foo() function.

I want to make it so if a "better" foo() exists (one from a Policy class) that one will always be chosen.

Here is some code:

// logic for determining if a class has a member function (not really related)
template <class> struct sfinae_true: std::true_type{};
template <class T> static auto test_foo(int) -> sfinae_true<decltype(std::declval<T>().foo())>;
template <class> static auto test_foo(long) -> std::false_type;
template <class T> struct has_foo : decltype(test_foo<T>(0)){};

class AlwaysBase{ public: void foo(){ cout << "in AlwaysBase foo()" << endl; } };
class BPolicy{};
class CPolicy{ public: void foo(){ cout << "in Cpolicy foo()" << endl; } };

template<typename T, typename ...Args>
class A: public T, public A<Args...>
{
public:
    using std::conditional_t<has_foo<T>::value, T, AlwaysBase>::foo;
};

template<typename T>
class A<T>: public T, public AlwaysBase
{
public:
    using std::conditional_t<has_foo<T>::value, T, AlwaysBase>::foo;
};

int main()
{
    A<BPolicy> b;
    b.foo();     // outputs: In AlwaysBase foo()

    A<CPolicy> c;
    c.foo();     // outputs: In CPolicy foo()
    
    A<CPolicy, BPolicy> cb;
    cb.foo();    // outputs: In CPolicy foo()

    A<BPolicy, CPolicy> bc;
    bc.foo();    // outputs: In AlwaysBase foo() // I WANT THIS TO OUTPUT!!!!: In CPolicy foo
}

I understand what is happening here, when BPolicy is first, that using statement gets used in the variadic args class first and shadows any preceding using statement that would be found in the terminating base class (namely the CPolicy::foo).

I would like for the order to not matter when specifying policies, and if a Policy has a determinant, that one should always be chosen first over the AlwaysBase::foo.


Solution

  • Use recursion. If empty lists are allowed, this is very easy:

    // recursive invariant: As<Ts...>::foo is always the foo from the first capable Ts or from AlwaysBase
    template<typename... Ts>
    class A : public AlwaysBase { // template<> class A<>
    public:
        // there are no capable Ts
        using AlwaysBase::foo;
    };
    template<typename T, typename... Ts>
    class A<T, Ts...> : public T, public A<Ts...> {
    public:
        // from the A<Ts...> superclass, not AlwaysBase
        // try to use T, fall back to Ts, and only then fall back to AlwaysBase
        using std::conditional_t<has_foo<T>::value, T, A<Ts...>>::foo;
    };
    

    Constraining to nonempty lists is a bit more annoying:

    template<typename T, typename... Ts>
    class A : public T, public A<Ts...> {
    public:
        using std::conditional_t<has_foo<T>::value, T, A<Ts...>>::foo;
    };
    template<typename T>
    class A<T> : public T, public AlwaysBase {
    public:
        using std::conditional_t<has_foo<T>::value, T, AlwaysBase>::foo;
    };
    

    Order still matters, though. If two policies provide foo, the first one wins.