希望得到一些关于为什么允许/不允许以下内容的详细解释。
#include <type_traits>
#include <iostream>
template<typename T>
std::enable_if_t<std::is_arithmetic_v<T>> foo(T v) {
std::cout << "arithmetic version called\n";
}
template<typename T>
std::enable_if_t<!std::is_arithmetic_v<T>> foo(T v) {
std::cout << "non arithmetic version called\n";
}
int main() {
foo(33);
foo("hello");
}关于上面的代码示例,为什么这是允许的,因为重载解析不会启动,因为函数只根据返回类型不同?
接下来,使用表达式SFINAE,我必须确保我消除了重载的歧义,如下面的代码片段所示。
#include <type_traits>
#include <iostream>
template<typename T, std::enable_if_t<std::is_arithmetic_v<T>>* p = nullptr>
void foo(T v) {
std::cout << "arithmetic version called\n";
}
template<typename T, std::enable_if_t<!std::is_arithmetic_v<T>>* p = nullptr>
void foo(T v) {
std::cout << "non arithmetic version called\n";
}
int main() {
foo(33);
foo("hello");
}所以我想这个问题更多的是它的工作原理(语言规则和如何做它)。我使用这些技术已经有一段时间了,并且经常会绞尽脑汁地考虑哪些语言规则能够实现这一点。
提前谢谢。
#include <type_traits>
#include <iostream>
#include <string>
template<typename T>
auto increment_or_self_(T a, int i) -> std::decay_t<decltype(++std::declval<T&>(), std::declval<T>())> {
++a;
return a;
}
template<typename T>
auto increment_or_self_(T a, ...) -> T {
return a;
}
template<typename T>
T increment_or_self(T a) {
return increment_or_self_(a, 0);
}
int main() {
std::cout << increment_or_self(33) << std::endl;
std::cout << increment_or_self(std::string("hello")) << std::endl;
}在上面的increment_or_self中有一个虚拟的arg,以确保重载是不含糊的。
另外,为什么我们在移动EnableIf时必须使用指针,如下所示?
发布于 2021-12-29 16:34:26
注意,没有指定不同的返回值,这两个函数都返回一个空值。所以这里没有问题。SFINAE将确保其中一个函数不会产生可编译的返回类型(但不会给出错误,因为substition不是错误)。
不要专注于SFINAE,c++一直在改进元模板编程技术,在这种情况下,我只会使用一个if constexpr解决方案。如下所示:
#include <type_traits>
#include <iostream>
template<typename T>
void foo(const T& v) // <== I prefer not to use pass by value for template parameters so I pass by reference (avoid copying)
{
if constexpr (std::is_arithmetic_v<T>)
{
std::cout << "arithmetic version called\n";
}
else
{
std::cout << "non arithmetic version called\n";
}
}
int main()
{
foo(33);
foo("hello");
return 0;
}发布于 2021-12-29 17:17:07
第一个示例在重载解析方面没有问题,因为在不同的返回类型中,没有两倍于相同的函数,因为SFINAE的内容已经在每个实例化中禁用了这两个函数中的一个。即使SFINAE表达式将导致两种不同的返回类型,但这并不重要,因为这两个实例化函数具有不同的签名,因为它们具有不同的输入参数。
我修改了您的示例,这也将是很好的格式(请参阅此更改的不同返回类型)
std::enable_if_t<std::is_arithmetic_v<T>, float> foo(T ) {
std::cout << "arithmetic version called\n";
return 1.1;
}
template<typename T>
std::enable_if_t<!std::is_arithmetic_v<T>, int> foo(T ) {
std::cout << "non arithmetic version called\n";
return 1;
}
int main() {
foo(33);
foo("hello");
std::cout << std::is_same_v< float, decltype(foo(33))> << std::endl;
std::cout << std::is_same_v< int, decltype(foo("Hallo"))> << std::endl;
}您的问题是:“为什么我们在移动EnableIf时必须使用指针,如下所示?”非常简单:只有一个参数的std::enable_if以void结尾。你根本就不能将一个值赋值为无效。您可以将您的代码更改为std::enable_if_t<!std::is_arithmetic_v<T>, bool> p = true,因为现在您的std::enable_if会产生一个bool,您可以在其中分配一个类似true的值。
使用C++20,您可以通过使用concepts来简化很多事情
template <typename T> concept arithmetic = std::is_arithmetic_v<T>;
template< arithmetic T>
void foo(T) {
std::cout << "arithmetic version called\n";
}
void foo(auto) {
std::cout << "non arithmetic version called\n";
}
int main() {
foo(33);
foo("hello");
}正如您所看到的,我们不需要定义“非算术”概念,因为我们可以简单地信任规则,即如果能够实例化更多的模板,将使用更指定的模板。
https://stackoverflow.com/questions/70521969
复制相似问题