2

Suppose an algorithm that has a policy FooPolicy. Policy classes that implement this policy feature a static member function foo, but, for some of them, foo takes an int argument, while for others it does not. I am trying to enable the use of these policy classes with differing interfaces by means of constexpr static data members:

struct SimpleFoo {
    static constexpr bool paramFlag = false;
    static void foo() {
        std::cout << "In SimpleFoo" << std::endl;
    }
};

struct ParamFoo {
    static constexpr bool paramFlag = true;
    static void foo(int param) {
        std::cout << "In ParamFoo " << param << std::endl;
    }
};

template <typename FooPolicy>
struct Alg {
    void foo() {
        if (FooPolicy::paramFlag) FooPolicy::foo(5);
        else FooPolicy::foo();
    }
};

int main() {
    Alg<ParamFoo> alg;
    alg.foo();
    return 0;
}

This code does not compile. gcc 4.8.2 gives the error:

no matching function for call to ‘ParamFoo::foo()’

else FooPolicy::foo();

The else clause gets compiled despite the fact that it is known at compile time that FooPolicy::paramFlag is true. Is there a way to make it work?

Community
  • 1
  • 1
AlwaysLearning
  • 7,257
  • 4
  • 33
  • 68
  • 3
    Yes, provide two overloads which are conditionally selected via e.g. tag dispatching – Columbo Sep 16 '15 at 09:37
  • 1
    You can even dispense with the flag and just SFINAE it. – T.C. Sep 16 '15 at 09:38
  • You cannot agree on an `int param = 0` parameter for all functions? – Bo Persson Sep 16 '15 at 09:41
  • A different approach is to use objects, not types, in the way the standard library does for its policies (comparators, hashers, allocators). Then those objects can be stateful. – Kerrek SB Sep 16 '15 at 09:50
  • @KerrekSB It's difficult to see what you mean from a short comment like this. How does using objects take care of the fact that the two policies have different interfaces? I would very much appreciate if you turn your comment into a reply. – AlwaysLearning Sep 16 '15 at 10:30
  • @MeirGoldenberg: You give every policy the same interface, but make the "argument" part of the state: `X x;` vs `X y(Policy2(5))`. Just like stateful allocators, if that makes sense. – Kerrek SB Sep 16 '15 at 10:53
  • @KerrekSB I think I understand the idea. The problem with this design is that the user of `X` does not know in advance the value of the argument that `X::foo` will need to pass to the policy (5 in the example). In fact, `X::foo` may need to call `Policy2::foo` many a time with different values of the argument (i.e. different states)... – AlwaysLearning Sep 16 '15 at 11:29

2 Answers2

5

Is there a way to make it work?

One solution is to use tag-dispatching:

#include <type_traits>

template <typename FooPolicy>
struct Alg {
    void foo() {
        foo(std::integral_constant<bool, FooPolicy::paramFlag>{});
    }    
private:
    void foo(std::true_type) {
        FooPolicy::foo(5);
    }
    void foo(std::false_type) {
        FooPolicy::foo();
    }
};

DEMO

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
3

You could dispense with the flag entirely and use expression SFINAE:

template <typename FooPolicy>
struct Alg {
    template <typename T=FooPolicy> //put FooPolicy in immediate context
    //SFINAEd out if that call is not valid
    auto foo() -> decltype(T::foo(),void()) {
        FooPolicy::foo();
    }

    template <typename T=FooPolicy>
    auto foo() -> decltype(T::foo(0),void()) {
        FooPolicy::foo(6);
    }
};
TartanLlama
  • 63,752
  • 13
  • 157
  • 193