用概念重载函数

Overloading functions with concepts

(我正在学习概念和模板,所以如果我有什么非常错误的地方,请纠正我。)我有一个将概念作为参数的函数。我现在正试图重载这个采用更具体概念的函数。那会做“更具体的事情”或调用不太具体的功能。

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);
  }
}

有什么方法可以显式地告诉编译器要调用哪个重载(比如 someFunc<Concept1>(x) 不起作用),还是仅取决于传递的对象的类型?假设我无法将 x 转换为更通用的类型,并且更通用的函数/概念不知道这个更具体的函数/概念,因此他们不能用约束排除它。 编辑:这些函数应该在同一个(全局)命名空间中。

通常的解决方法是使用单独的辅助函数:

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);
  }
}

另一个没有分离功能的解决方法是使用 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);
}

如果可能(主要取决于两个概念),另一种解决方法是创建仅满足 Concept1 但不满足 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(); }
    // ...
};

然后你可能会做

void someFunc(const MoreSpecificConcept auto& x)
{
    if (...) {
        // do specific stuff
    } else {
        // do the more general thing:
        someFunc(AsConcept1{x});
    }
}

这是我的版本 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;
}

这是我的 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;
}

我觉得这个版本很好很干净:不需要额外的功能或者这个讨厌的if