Reputation: 941
(I'm learning concepts and templates so correct me if I'm very wrong with something.) I have a function that takes a concept as parameter. I'm now trying to overload this function that takes a more specific concept. That would do "something more specific" or call the less specific function.
template<typename T>
concept Concept1 = ...;
template<typename T>
concept MoreSpecificConcept = ...;
Concept1 <T> &&
...;
//...
void someFunc(const Concept1 auto& x)
{
//do general stuff
}
void someFunc(const MoreSpecificConcept auto& x)
{
if(...)
{
//do specific stuff
}
else
{
//do the more general thing:
// Problem. Trying to call itself:
someFunc(x);
}
}
Is there any way to explicitly tell the compiler which overload to call (like someFunc<Concept1>(x)
which doesn't work), or is it solely dependent on the type of the passed object? Lets say that I can't cast x
to the more-general type and that the more general function / concept don't know about this more specific function / concept so they can't exclude it with a constraint.
Edit: the functions are meant to be within the same (global) namespace.
Upvotes: 8
Views: 936
Reputation: 37697
Here is my version of Minimal Complete Verifiable Example
#include <iostream>
#include <type_traits>
template <typename T>
concept aritmetic = std::is_arithmetic_v<T>;
template <typename T>
concept real = std::is_floating_point_v<T>;
template<aritmetic T>
void foo(T x)
{
std::cout << __PRETTY_FUNCTION__ << " x + 1 = " << x + 1 << '\n';
}
#ifdef HAS_OVERLOAD
void foo(real auto x)
{
std::cout << __PRETTY_FUNCTION__ << " x / 2 = " << x / 2 << '\n';
}
#endif
int main()
{
foo(1);
foo(1.1);
return 0;
}
And here is my idea how to fix it:
#include <iostream>
#include <type_traits>
template <typename T>
concept aritmetic = std::is_arithmetic_v<T>;
template <typename T>
concept real = std::is_floating_point_v<T>;
template<aritmetic T>
void foo(T x) requires (!real<T>)
{
std::cout << __PRETTY_FUNCTION__ << " x + 1 = " << x + 1 << '\n';
}
void foo(real auto x)
{
std::cout << __PRETTY_FUNCTION__ << " x / 2 = " << x / 2 << '\n';
}
int main()
{
foo(1);
foo(1.1);
return 0;
}
I think this version is nice and clean: no extra function is needed or this nasty if
.
Upvotes: 0
Reputation: 217255
If possible (mostly depends of both concepts), Another work-around is to create wrapper which only satisfies Concept1
but not MoreSpecificConcept
:
template <Concept1 T>
struct AsConcept1
{
T& t;
// operations to satisfy Concept1 (but not MoreSpecificConcept)
// using some_type = typename Concept1::some_type
// void bar() { t.bar(); }
// ...
};
and then you might do
void someFunc(const MoreSpecificConcept auto& x)
{
if (...) {
// do specific stuff
} else {
// do the more general thing:
someFunc(AsConcept1{x});
}
}
Upvotes: 1
Reputation: 41770
The usual workaround for that would be a separated helper function:
void somefunc(const Concept1 auto& x) {
// general stuff
}
void somefuncSpecific(const Concept1 auto& x) {
somefunc(x);
}
void someFuncSpecific(const MoreSpecificConcept auto& x)
{
if(...)
{
//do specific stuff
}
else
{
//do the more general thing:
somefunc(x);
}
}
Another workaround without having separated functions is to use if constexpr
:
void someFuncSpecific(const Concept1 auto& x)
{
if constexpr(MoreSpecificConcept<decltype(x)>)
{
if (...)
{
//do specific stuff
// skip the rest:
return;
}
}
//do the more general thing:
somefunc(x);
}
Upvotes: 7