带有别名的 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 都支持很好的概念?
我认为你的问题是 FloatingPoint
和 Integral
直接依赖于 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/gLc7YE 。 if 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;
}
我有这个代码:
#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 都支持很好的概念?
我认为你的问题是 FloatingPoint
和 Integral
直接依赖于 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/gLc7YE 。 if 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;
}