首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SFINAE歧义

SFINAE歧义
EN

Stack Overflow用户
提问于 2021-12-29 16:08:12
回答 2查看 78关注 0票数 0

希望得到一些关于为什么允许/不允许以下内容的详细解释。

代码语言:javascript
复制
#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,我必须确保我消除了重载的歧义,如下面的代码片段所示。

代码语言:javascript
复制
#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");
}

所以我想这个问题更多的是它的工作原理(语言规则和如何做它)。我使用这些技术已经有一段时间了,并且经常会绞尽脑汁地考虑哪些语言规则能够实现这一点。

提前谢谢。

代码语言:javascript
复制
#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时必须使用指针,如下所示?

EN

回答 2

Stack Overflow用户

发布于 2021-12-29 16:34:26

注意,没有指定不同的返回值,这两个函数都返回一个空值。所以这里没有问题。SFINAE将确保其中一个函数不会产生可编译的返回类型(但不会给出错误,因为substition不是错误)。

不要专注于SFINAE,c++一直在改进元模板编程技术,在这种情况下,我只会使用一个if constexpr解决方案。如下所示:

代码语言:javascript
复制
#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; 
}
票数 1
EN

Stack Overflow用户

发布于 2021-12-29 17:17:07

第一个示例在重载解析方面没有问题,因为在不同的返回类型中,没有两倍于相同的函数,因为SFINAE的内容已经在每个实例化中禁用了这两个函数中的一个。即使SFINAE表达式将导致两种不同的返回类型,但这并不重要,因为这两个实例化函数具有不同的签名,因为它们具有不同的输入参数。

我修改了您的示例,这也将是很好的格式(请参阅此更改的不同返回类型)

代码语言:javascript
复制
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_ifvoid结尾。你根本就不能将一个值赋值为无效。您可以将您的代码更改为std::enable_if_t<!std::is_arithmetic_v<T>, bool> p = true,因为现在您的std::enable_if会产生一个bool,您可以在其中分配一个类似true的值。

使用C++20,您可以通过使用concepts来简化很多事情

代码语言:javascript
复制
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");
}

正如您所看到的,我们不需要定义“非算术”概念,因为我们可以简单地信任规则,即如果能够实例化更多的模板,将使用更指定的模板。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70521969

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档