Reputation: 9289
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.
Upvotes: 4
Views: 1773
Reputation: 179799
If you want to use a static_assert
, that is possible:
static_assert(static_cast< void (T::*)(uint32_t) >(&T::update),
"Need void T::update(uint32_t");
The cast checks if overload resolution is possible, i.e. if the right signature is present.
Upvotes: 0
Reputation: 2992
You don't need concepts for this, although those help. This code will happily works in C++11.
#include <type_traits>
#include <cstdint>
using namespace std;
struct A {
void Update(uint32_t);
int32_t GetResult() const;
};
struct B {
};
using namespace std;
template <typename T, typename = typename enable_if<
is_same<decltype(&T::Update), void (T::*)(uint32_t)>::value &&
is_same<decltype(&T::GetResult), int32_t (T::*)() const>::value
>::type>
struct CLS
{
};
using namespace std;
int main() {
CLS<A> a; // works
CLS<B> b; // fails to compile
return 0;
}
Note, that this code checks exact function signature, so Update(int)
won't pass.
EDIT: added const to getResult
Upvotes: 5
Reputation: 20579
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)
{
// ...
}
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)
{
}
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.
Upvotes: 6