2

Looking at a simple template scenario like this:

class A {
    public:
        int work();
};

class B {
    public:
        int work();
};

class ObjectManager {
    public:
        static void manage( A& obj );
        // manage not defined for class B 
};

template<class T>
doStuff( T t ) {
    t.work();
    ....
    ObjectManager::manage(t);
};


A a;
B b;

doStuf(a);
doStuff(b);

I am wondering, what is the cleanest way to implement a conditional call to ObjectManager::manage? The template function should determine in compile-time if ObjectManager::manage(T t) is defined for the given T and activate some lines of code only when it is. I guess there are solutions with some more nested template calls, but it would be the best for me just to keep the function in one piece.

philipp
  • 1,745
  • 1
  • 14
  • 25
  • I believe it goes something along the lines of [enable_if...](http://en.cppreference.com/w/cpp/types/enable_if) – smac89 Oct 10 '14 at 14:43
  • I think this link can help you : http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence – Garf365 Oct 10 '14 at 14:48

1 Answers1

5

Note that currently your class members are all private. They should be made public.

template<class T>
auto call_manage(T* t) -> decltype(ObjectManager::manage(*t)){
    return ObjectManager::manage(*t);
}

void call_manage(...) {}

template<class T>
void doStuff( T t ) {
    t.work();
    //....
    call_manage(std::addressof(t)); // smack whoever overloaded unary 
                                    // operator & in the head first
}

If the expression ObjectManager::manage(*t) is well-formed, substitution succeeds for the function template, which is always a better match than ..., so it is called. Otherwise, the do-nothing overload is the only viable function.

Demo.


For those who like references:

template<class T>
auto call_manage(T& t) -> decltype(ObjectManager::manage(t)){
    return ObjectManager::manage(t);
}

template<class... T>
void call_manage(T&&...) {}

template<class T>
void doStuff( T t ) {
    t.work();
    //....
    call_manage(t);
}

In this case, the single-argument overload is more specialized than the variadic version by the partial ordering rules for function templates, and is selected by overload resolution if both are equally viable. You can also make it take T&& t and call manage(std::forward<T>(t)), for perfect forwarding.

Demo.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • @Jarod42 because `...` can break with a reference. It can be fixed by making the fallback a variadic template instead, but it feels a bit odd to have a fallback that complicated... – T.C. Oct 10 '14 at 15:00
  • @T.C. "can" break or "does" break? In other words, if it is only "can" break, when does it break and when does it not break? – kec Oct 10 '14 at 15:29
  • 3
    @kec A plain `...` passes by value, so 1) it will make a copy and 2) passing an object of a class type "having a nontrivial copy constructor, a non-trivial move constructor, or a non-trivial destructor" is "conditionally-supported with implementation-defined semantics". So if the copy ctor or the dtor aren't trivial, it may not compile and even it does compile it would incur a copy. – T.C. Oct 10 '14 at 15:33