I'm trying to check if a (member) function has a default assignment for it's function arguments. But I can't seem to find a type trait that gives me that information. (something like std::has_default_assignment
).
Demonstration (non functional) of what I'm trying to do.
struct TypeA {
void process(int a, int b) const {};
};
struct TypeB {
void process(int a, int b = 0) const {};
};
template<typename T, typename A1, typename A2>
using MemFcn = void(T::*)(A1, A2) const;
#include <type_traits>
template<typename T, typename A1, typename A2>
typename std::enable_if<std::has_default_assignment<A2>::value>::type
TestProcess(MemFcn<T, A1, A2> fcn) {
fcn(1);
};
template<typename T, typename A1, typename A2>
typename std::enable_if<!std::has_default_assignment<A2>::value>::type
TestProcess(MemFcn<T, A1, A2> fcn) {
fcn(1, 2);
};
template<typename T>
void TestConcepts(T)
{
TestProcess(&T::process);
}
int main(void) {
// Should call TypeA::process(1,2);
TestConcepts(TypeA{});
// Should call TypeB::process(1)
TestConcepts(TypeB{});
return 0;
}
How can I detect if a function parameter has a default assignment value?
A function pointer does not store the information of default arguments. Even if the function pointed to has default arguments, you will get compile time error saying wrong number of arguments.
You can however test if a specific member function can be called with different number of parameters. This does not guarantee that there is a default argument though, it could also be 2 different overloads that takes different number of parameters.
#include <type_traits>
#include <iostream>
struct TypeA {
void process(int a, int b) const {};
};
struct TypeB {
void process(int a, int b = 0) const {};
};
template <typename T, typename U = void>
struct has_default_arg : std::false_type {};
template <typename T>
struct has_default_arg<T, std::void_t<decltype(std::declval<T>().process(1))>> : std::true_type {};
template<typename T>
void TestProcess() {
if constexpr (has_default_arg<T>::value) {
std::cout << "default arg\n";
T{}.process(1);
}
else {
std::cout << "no default arg\n";
T{}.process(1,2);
}
};
template<typename T>
void TestConcepts(T)
{
TestProcess<T>();
}
int main(void) {
// Should call TypeA::process(1,2);
TestConcepts(TypeA{});
// Should call TypeB::process(1)
TestConcepts(TypeB{});
return 0;
}
This is using some c++17
features, but could be written in c++11
as well with some additional efforts.