3
template <typename T, typename Predicate, typename Operation>
void Foo(T& entity, Predicate pred, Operation op)
{
    if (pred(entity))
    {
        op(entity);
    }

    // and blah
}
template <typename T, typename Predicate, typename Operation>
void Foo(const T& entity, Predicate pred, Operation op)
{
    if (pred(entity))
    {
        op(entity);
    }

    // and blah
}

P.S.

T& entity + pred(const T& entity) + op(const T& entity) is acceptable.

const T& entity + pred(T& entity) + op(T& entity) should raise compile error.

Solutions using C++11 is ok.


Examples here:

class MyEntity
{
public:
    MyEntity(int e):e(e){}
    int e;
};

MyEntity a = 1234;
MyEntity& ra = a;
const MyEntity& cra = a;
auto pred = [](const MyEntity& i)
{
    return true;
};
auto cop = [](const MyEntity& i)
{
    cout<<i.e<<endl;
};
auto op = [](MyEntity& i)
{
    ++i.e;
    cout<<i.e<<endl;
};
Foo(ra, pred, op);   // ok
Foo(ra, pred, cop);  // ok
Foo(cra, pred, cop); // ok
Foo(cra, pred, op);  // error
Marson Mao
  • 2,935
  • 6
  • 30
  • 45
  • Are `pred` and `op` overloaded for `T&` and `const T&`? – Bathsheba Jan 29 '15 at 09:28
  • @Bathsheba `pred` and `op` should follow constness of `entity`, but not need to be the same, like my example of `T& entity` with `pred(const T& entity)`. – Marson Mao Jan 29 '15 at 09:31
  • I think then, all you can do is to code *blah* in a function. But good question. – Bathsheba Jan 29 '15 at 09:31
  • If the code in both functions are exactly equal, why not have the non-const function call the const-function? – Some programmer dude Jan 29 '15 at 09:40
  • @JoachimPileborg yes that's what I am looking for, but I can't figure out how to manage the `const_cast` to achieve it. I think this case is not the same as the common member function case (well at least in my opinion). – Marson Mao Jan 29 '15 at 09:44
  • `const_cast(entity)` should work. – Some programmer dude Jan 29 '15 at 09:52
  • But that assumes that the `pred` and `op` overloads do the same thing, which, in fully generality, may not be the case. But at least you've *introduced* `const` rather than removed it. – Bathsheba Jan 29 '15 at 09:55
  • 2
    why is the non-const overload needed ? ie. what does it do differently ? – Sander De Dycker Jan 29 '15 at 10:02
  • 1
    @SanderDeDycker The non-const is for some `op` that accepts `T&`, the `op` might cause some changes on `T& entity`, so I would like `T& entity` to be with `op(T& entity)` (of course okay with `op(const T& entity)` as well). But the contrary should be forbidden, i.e. `const T& entity` with `op(T& entity)`. – Marson Mao Jan 29 '15 at 10:11

2 Answers2

4

You can use forwarding reference (aka "universal reference"):

template <typename T, typename Predicate, typename Operation>
void Foo(T&& entity, Predicate pred, Operation op)
{
    if (pred(entity))
    {
        op(std::forward<T>(entity));
    }

    // and blah
}
Anton Savin
  • 40,838
  • 8
  • 54
  • 90
  • ok, this one is cool, it works; but I found that `T` is `T const &` when giving `const T& entity` with `op(const T& entity)`; could you please tell me why? I mean why `T&&` is `T const &` not `const T &`? – Marson Mao Jan 29 '15 at 10:23
  • @MarsonMao `T const` and `const T` are the same type, so are `T const&` and `const T&`. – Anton Savin Jan 29 '15 at 10:24
  • oooh! So is `T const` and `const T` has nothing to do with something like `const int* i` and `int* const i`? What keyword should I use to google that `T const` is the same type with `const T`? Sorry for asking so much. – Marson Mao Jan 29 '15 at 10:27
  • @MarsonMao yes, `const int* i` is the same as `int const* i` and not the same as `int* const i`. I think any good C++ book contains this information. – Anton Savin Jan 29 '15 at 10:30
  • Thanks a lot that's great help. Wondering if you could answer a little more; Actually my original intention was to overload `void Foo(Entity& entity, Predicate pred, Operation op)` and `void Foo(const Entity& entity, Predicate pred, Operation op)`. I tried to make it `void Foo(Entity&& entity, Predicate pred, Operation op)` but can't compile. How to make it work if I am not using `T&&`? – Marson Mao Jan 29 '15 at 10:43
  • 1
    @MarsonMao you better post it with full context as another question. – Anton Savin Jan 29 '15 at 10:48
  • I don't think this will work if T is an rvalue reference. You'll forward `entity` to `pred` and then call `op` with an expired value. – pelletjl Jan 11 '16 at 17:20
  • @pelletjl `pred` should take a const reference by it's nature, that is it shouldn't move from its parameter. – Anton Savin Jan 11 '16 at 21:04
  • @AntonSavin Just like `move`, I believe arguments should only be forwarded once: http://stackoverflow.com/q/20195908/1968586 – pelletjl Jan 11 '16 at 22:57
  • @pelletjl Alright, this can be actually fixed by removing `std::forward` from `pred()` invocation. Edited the answer and thanks for pointing this out. – Anton Savin Jan 11 '16 at 23:46
3

You can have just the non-const Foo, and leave the const validation to the specific pred and op.

If either pred or op require a non-const entity, and the entity is const (as in the 4th call in your example code), the compiler will throw an error.

Sander De Dycker
  • 16,053
  • 1
  • 35
  • 40
  • @AntonSavin : that's a different issue. This question is about `const`-ness (or at least that's how I understood it), not about assigning an rvalue to a non-const reference. Try your example with `const int i = 1; foo(i);`. – Sander De Dycker Jan 29 '15 at 10:41
  • Of course, if rvalues need to be supported as parameter to `Foo`, then the matter is different, and @AntonSavin's answer is more appropriate. But the example in the op doesn't indicate that that's needed. – Sander De Dycker Jan 29 '15 at 10:44
  • @MarsonMao : it will - give it a try with your own example code eg. (assuming that by "work" you mean that it throws an error as appropriate) – Sander De Dycker Jan 29 '15 at 10:45
  • Damn it, it works as well, you are right. I think I should ask another question about my real intention...actually I want to directly provide `T`, like `void Foo(Entity& entity, Pred pred, Op op) and `void Foo(const Entity& entity, Pred pred, Op op)`. – Marson Mao Jan 29 '15 at 10:57
  • @MarsonMao : if the `entity` parameter is not templated, then you will indeed need both overloads. – Sander De Dycker Jan 29 '15 at 11:04
  • @SanderDeDycker Ok...so there is no way to avoid the duplicated codes then? – Marson Mao Jan 29 '15 at 11:59
  • @MarsonMao : apart from factoring out common parts, not really. – Sander De Dycker Jan 29 '15 at 12:48