Jakube
Jakube

Reputation: 3574

Checking for a templated static method via concepts

I want to write a concept that checks if the type has a static method called foo. That method will have a templated parameter (the function will be called multiple times later with different parameter types).

Because of that templated parameter, it's quite difficult to check it. For the start, I thought I only check if there is a member at all with that name.

The following code compiles with Clang, but doesn't compile with GCC, because it cannot resolve the address of the overloaded function T::foo.

template <typename T>
concept HasFoo = requires { T::foo; };

class Bar {
public:
    template <typename T>
    static void foo(T t);
};

static_assert(HasFoo<Bar>);

How do you correctly check for the existence of a templated static method (working in Clang and GCC)? And ideally, can you even check more than this? Like checking if the return type is void, or if it is callable.

One way would be to include the templated type into the concept, but as I want to use the method with multiple different types. So checking with only one type, like in the following code, is not enough.

template <typename T, typename T2>
concept HasFoo = requires { T::template foo<T2>; };

static_assert(HasFoo<Bar, int>);

Upvotes: 0

Views: 1164

Answers (1)

max66
max66

Reputation: 66230

How do you correctly check for the existence of a templated static method (working in Clang and GCC)? And ideally, can you even check more than this? Like checking if the return type is void, or if it is callable.

I do have some constraints on template arguments, for the sake of the simplified example in the question we can just assume it's an integer type.

To check if the class support a static template method foo() that is callable with an integer and return void, you can simply check

template <typename T>
concept HasFoo = std::is_same_v<decltype(T::foo(0)), void>;

If you also want to be sure that the foo() method is a template one, I suppose you can also check that converting &T::foo to different function pointer types you get different values, so (for example)

(    (void*)(&T::template foo<int>)
  != (void*)(&T::template foo<long>))

Combining the two requirements,

template <typename T>
concept HasFoo =    (    (void*)(&T::template foo<int>)
                      != (void*)(&T::template foo<long>))
                 && std::is_same_v<decltype(T::foo(0)), void>;

With

struct Bar1 
 { template <typename T> static void foo (T) {} };

struct Bar2 
 { static void foo (int) {} };   

struct Bar3
 { template <typename T> static T foo; };

template <typename T>
T Bar3::foo;

struct Bar4
 { template <typename T> static int foo (T) { return 0; } };

you have

static_assert(HasFoo<Bar1>);
static_assert(not HasFoo<Bar2>); // not because foo() isn't template
static_assert(not HasFoo<Bar3>); // not because foo isn't callable
static_assert(not HasFoo<Bar4>); // not becasue foo() return int

Upvotes: 2

Related Questions