7

I have a template that calls a member function. How do I check with static_assert that the method exists?

struct A {

};

struct B {
    int foo() { return 42; } };

template <typename T> struct D {
    static_assert(/* T has foo */, "T needs foo for reasons");

    int bar() {
       return t.foo();
    }

    T t; };

int main() {
    D<A> d;

    std::cout << d.bar() << std::endl;

    return 0; }

I know this will just generate a compiler error that A does not have foo but I would like to check and give a better error output using static_assert.

Mochan
  • 1,059
  • 1
  • 8
  • 21
  • https://stackoverflow.com/questions/257288/is-it-possible-to-write-a-template-to-check-for-a-functions-existence – Cruz Jean Mar 15 '19 at 22:45
  • 1
    Possible duplicate of [Is it possible to write a template to check for a function's existence?](https://stackoverflow.com/questions/257288/is-it-possible-to-write-a-template-to-check-for-a-functions-existence) – bartop Mar 15 '19 at 22:47
  • I think a compiler error is fine if it's clear. And `error: no member named 'foo' in 'A'` seems pretty clear. – Raymond Chen Mar 16 '19 at 00:12
  • C++20 solves this with concepts, if you’re going to upgrade to that when it’s out. – Daniel H Mar 16 '19 at 00:13
  • There is [experimental support](https://en.cppreference.com/w/cpp/experimental/is_detected) for this already. Very easy to implement your own version. – super Mar 16 '19 at 17:53

2 Answers2

5

Since you use static_assert I assert that you are using at least C++11. This allows to write something like this:

#include <type_traits>

template<class ...Ts>
struct voider{
    using type = void;
};

template<class T, class = void>
struct has_foo : std::false_type{};

template<class T>
struct has_foo<T, typename voider<decltype(std::declval<T>().foo())>::type> : std::true_type{};

And you just use static field value (has_foo<your_type>::value) - if it's true then your type has function foo.

bartop
  • 8,927
  • 1
  • 18
  • 46
  • 2
    Two comments: 1. In C++17 you can use [`std::void_t`](https://en.cppreference.com/w/cpp/types/void_t) and even before that you can write `voider` somewhat more nicely. 2. You can also use more type traits to ensure that *calling* `T::foo` returns an `int` or something convertible. – Daniel H Mar 16 '19 at 00:07
1

Constraint templates has been a long discussion - ever since 2005 or so - on std forums. But the outcome has yet to wait til C++20.

Alexis Wilke
  • 15,168
  • 8
  • 60
  • 116
Red.Wave
  • 1,427
  • 6
  • 8