I am making a type erased class TypeErased
using std::variant
, that could be an integer, a complex number, a vector, or an error.
While I am making the addition operator (which uses std::visit
), I have come into a problem that I don't want certain types to be added together, and it seems very impractical to create overloads for every single unwanted combination.
For example, it should be able to add an integer with a complex number, but not a vector and an integer. To do this, I have tried to use SFINAE (I believe) to create a struct that can tell if a function overload exists:
#include<iostream>
#include<string>
void print(int) {}
template<auto FuncPtr, typename ...Args>
struct FuncExists
{
template<typename = decltype(FuncPtr(std::declval<Args>()...))>
constexpr static std::true_type test(int);
constexpr static std::false_type test(...);
constexpr static bool value = decltype(test(0))::value;
};
int main()
{
std::cout<<"print string exists: "<<FuncExists<print, std::string>().value;
}
I expected that it would be able to tell whether it was valid or not, but whenever I try to test it on something that shouldn't work, a compiler error is produced. When I try to pass std::string
:
error: cannot convert 'std::__cxx11::basic_string<char>' to 'int' in argument passing
12 | constexpr static auto test(int) -> decltype(FuncPtr(std::declval<Args>()...), std::true_type());
Why does this not work, and how could I change it to make it work?
For clarity, I am trying to see if a certain overload exists for ANY function (like operator+, sin etc). So if I try to add a variant with an integer and a variant with a vector, it can throw an exception
SFINAE happens only on "direct" context. Here, template are fixed by class, and it becomes an hard error.
As you have C++20, you might use requires
:
template <typename T>
concept is_printable = requires(T t) { print(t); };