c++ - 带有别名的 SFINAE,重载问题

标签 c++ sfinae

我有这段代码:

#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 作为参数或返回类型。还有其他方法可以“漂亮”地处理这个问题吗?或者我必须使用我之前写的两个解决方案之一吗?

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

最佳答案

我认为你的问题是 FloatingPointIntegral 直接依赖于 T。重新组织你的代码你可以得到以下内容。您可以在此处运行代码: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/gLc7YE . if constexpr 允许您非常轻松地只有一个函数甚至不同的返回类型。

#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() 使用不同的返回类型将解决您的问题(在此处运行代码: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;
}

关于c++ - 带有别名的 SFINAE,重载问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57169533/

相关文章:

c++ - 为模板类重载友元运算符 <<

c++ - 使用 C++17 检测仿函数的类型特征?

c++ - 使用 enable_if 选择类构造函数

c++ - eigen C++ 中的 Cholesky 分解 : how to get both the D vector and the inverse in one swoop?

c# - 通过套接字将 C# 结构发送到 C++

c++ - 可以取消引用这个指针吗?

c++ - 如果模板参数是另一个模板的某个实例,则类型特征测试

c++ - 你能检测出不可编译的代码吗?

c++ - "Overloading"带有 SFINAE 的构造函数

c++ - 使用带有 SSL (HTTPS) 的 Boost-Beast (Asio) http 客户端