Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to require a C++ template parameter to have some methods with a given signature?

Tags:

c++

I would like to define a class, which has one template parameter T. I would like to make sure, that T has a void Update(uint32_t) and a int32_t GetResult() const method. I know that the user will get an error when I try to call one of these methods, but I would like to make sure (possibly with a static assert) that they exists as soon as I can.

I was only able to find solutions to check if the class T is derived from some other class U, but that is not what I want. I wold like to allow any class, which has the mentioned methods.

like image 479
Iter Ator Avatar asked Sep 19 '25 09:09

Iter Ator


1 Answers

With concepts introduced in C++20, this is trivial with requires expressions and requires clauses. In the code, the keyword requires appears twice. The first introduces a requires clause, and the second introduces a requires expression.

template <typename T>
    requires requires (T& a, const T& b, uint32_t i) {
        a.Update(i);
        {b.GetResult()} -> int32_t;
    }
void foo(T& x)
{
    // ...
}

(live test)

Prior to C++20, you can write a trait class that deploys SFINAE:

template <typename T>
class foo_traits {
    static auto check(...) -> std::false_type;
    template <typename U>
    static auto check(U x) -> decltype((void)x.Update(uint32_t{}), std::true_type{});
public:
    static constexpr bool has_update = decltype(check(std::declval<T>()))::value;
};

Now you can use it like:

template <typename T>
void foo(T& x)
{
    static_assert(foo_traits<T>::has_update, "blah blah blah");
}

Or:

template <typename T>
std::enable_if_t<foo_traits<T>::has_update> foo(T& x)
{
}

(live test)

GetResult can be handled analogously.


Note: the above code only ensures that the expressions are valid, rather than ensuring the precise signatures. Of course you can also do that. Here's the approach with concepts:

template <typename T>
    requires requires (T& a, const T& b, uint32_t i) {
        a.Update(i);
        {&T::GetResult} -> std::int32_t (T::*)() const;
    }
void foo(T& x)
{
    // ...
}

Radosław Cybulski's answer already shows how to do this without concepts, so I am not showing it again.

like image 193
L. F. Avatar answered Sep 21 '25 02:09

L. F.