Vox Box
Vox Box

Reputation: 61

In a template, if a dependent name is a function, call it

In my TClass<T>::foo() function, I'd like to invoke a T instance if and only if T is a function type.

#include <iostream>
#include <functional>

template<class T>
struct TClass
{
    TClass(T value) : value(value) {}
    T value;
    void foo()
    {
        // if(value is std::function)
        //     call function;
    }
};

int main()
{
    TClass<int> t1{0};
    t1.foo();
    TClass<std::function<void()>> t2{[](){ std::cout << "Hello, World!\n"; }};
    t2.foo();
}

How can I do that?

Upvotes: 6

Views: 194

Answers (3)

KostasRim
KostasRim

Reputation: 2053

Why not partial specialization ?

Consider:

#include <iostream>
#include <functional>

template<class T>
struct TClass {
    TClass(T value) : value(value) {}
    T value;
    void foo() {
        std::cout << "T - other" << std::endl;
    }
};

template<class T>
struct TClass<std::function<T>> {
    TClass(std::function<T>  value) : value(value) {}
    std::function<T> value;
    void foo() {
        std::cout << "std::function" << std::endl;
    }
};

Upvotes: 2

Holt
Holt

Reputation: 37696

In C++17 you can do:

void foo() {
    if constexpr (std::is_invocable_v<T>) {
        value();
    }
}

If you only wants to allow std::function, you'll need your own trait, e.g.:

template <class T>
struct is_stdfunction: std::false_type {};

template <class T>
struct is_stdfunction<std::function<T>: std::true_type {};

template <class T>
constexpr bool is_stdfunction_v = is_stdfunction<T>::value;

// Then in foo():
void foo() {
    if constexpr (is_stdfunction_v<std::decay_t<T>>) {
        value();
    }
}

Upvotes: 4

Barry
Barry

Reputation: 304122

In C++11, the easiest way to do this is to re-deduce the value through a helper function:

template <typename U>
auto foo_helper(U const& f, int) -> decltype(f()) {
    return f();
}

template <typename U>
void foo_helper(U const&, long) {}

void foo() {
    foo_helper(value, 0);
}

The conversion from 0 to int is better than its conversion to long, so if the first overload is viable - it will be preferred. If the first overload isn't viable, then we call the second one.


If you really care only about std::function, then we can just have simpler overloads:

void foo_helper(std::function<void()> const& f) {
    f();
}

template <typename T>
void foo_helper(T const&) { }

void foo() {
    foo_helper(value);
}

Upvotes: 7

Related Questions