带有别名的 SFINAE,超载问题

SFINAE with alias, overloading issue

我有这个代码:

#include <iostream>
#include <type_traits>

template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
using FloatingPoint = T;

template <typename T, std::enable_if_t<std::is_integral_v<T>, bool> = true>
using Integral = T;

template <typename T> void f(Integral<T>) { std::cout << "integral" << std::endl; }

template <typename T> void f(FloatingPoint<T>) {
  std::cout << "floating point" << std::endl;
}

int main() {
  f(5);
  return 0;
}

此代码无法编译:

prog.cc:12:28: error: redefinition of 'f'
template <typename T> void f(FloatingPoint<T>) {
                           ^
prog.cc:10:28: note: previous definition is here
template <typename T> void f(Integral<T>) { std::cout << "integral" << std::endl; }
                           ^
prog.cc:17:3: error: no matching function for call to 'f'
  f(5);
  ^
prog.cc:12:28: note: candidate template ignored: substitution failure [with T = int]
template <typename T> void f(FloatingPoint<T>)

有点奇怪,因为它理解有替换失败,但他不想采取第一个重载。

所以我知道两种处理方法。第一件事是将 enable_if 作为参数或 return 类型。有没有其他方法以 "pretty" 方式处理这个问题?或者我必须使用我之前写的两个解决方案之一吗?

目标是编写干净的代码。我很想使用 C++20 中的概念,但似乎还不可能在所有地方使用它(我指的是 GCC、Clang 和 MSVC)https://en.cppreference.com/w/cpp/compiler_support。或者这个网站不是最新的并且 MSVC (2019)、GCC 10 和 clang 9 都支持很好的概念?

我认为你的问题是 FloatingPointIntegral 直接依赖于 T。 Re-organizing 你的代码你可以得到以下内容。您可以 运行 此处的代码:https://onlinegdb.com/By-T4bBfB .

#include <iostream>
#include <type_traits>

template<typename T>
using isFloatingPoint = std::enable_if_t<std::is_floating_point_v<T>, bool>;

template<typename T>
using isIntegral = std::enable_if_t<std::is_integral_v<T>, bool>;


template <typename T, isIntegral<T> = true>
void f(T) { 
    std::cout << "integral" << std::endl;
}

template <typename T, isFloatingPoint<T> = true>
void f(T) { 
    std::cout << "floatingPoint" << std::endl;

}

int main() {
  f(5);
  f(5.0);
}

或者,如果您使用的是 C++17,则可以使用 if constexpr。你说这在评论中是不可扩展的。在我看来是的,但很可能我不理解你所受的约束。为此,请参阅 https://godbolt.org/z/gLc7YEif constexpr 允许您只拥有一个功能,甚至可以很容易地拥有不同的 return 类型。

#include <type_traits>
#include <string>

template<typename T>
auto f(T){
    if constexpr (std::is_floating_point_v<T>){
        return 0.0f;
    } 
    else if constexpr (std::is_integral_v<T>){
        return 0;
    } else {
        return 0.0;
    }
}

int main() {
  static_assert(std::is_same_v<decltype(f(5)), int>, "ERROR 1");
  static_assert(std::is_same_v<decltype(f(5.0)), float>, "ERROR 2");
  static_assert(std::is_same_v<decltype(f(std::string{"JJ"})), double>, "ERROR 3");
  return 0;
}

最后我还要指出,在您的代码中为两个 f() 使用不同的 return 类型可以解决您的问题(运行 这里的代码:https://onlinegdb.com/By-9HZrMS):

#include <iostream>
#include <type_traits>

template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
using FloatingPoint = T;

template <typename T, std::enable_if_t<std::is_integral_v<T>, bool> = true>
using Integral = T;

template <typename T> int f(Integral<T>) { std::cout << "integral" << std::endl;  return 0;}

template <typename T> float f(FloatingPoint<T>) {
  std::cout << "floating point" << std::endl;
  return 0.0f;
}

int main() {
  f(5);
  f(5.0);
  return 0;
}