为什么'enable_if'不能在此处禁用此声明 [英] Why 'enable_if' cannot be used to disable this declaration here

查看:190
本文介绍了为什么'enable_if'不能在此处禁用此声明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

#include<string>
#include<type_traits>

template<typename... Args>
class C {
public:
    void foo(Args&&... args) {      

    }

    template<typename = std::enable_if_t<(0 < sizeof...(Args))>>
    void foo(const Args&... args) {     

    }
};

int main() {
    C<> c;
    c.foo();
    return 0;
}

以上代码按我的解释工作(由我:))并调用 void foo(Args&&...; args)在运行时在 msvc 2015 中,但是相同的代码甚至无法在两个 gcc 7.3中编译 clang 6.0.0 并显示错误:

Above code works as expacted (by me :)) and calls void foo(Args&&... args) at run-time in msvc 2015 but same code fails to even compile in both gcc 7.3 and clang 6.0.0 with error:


错误:在其中没有名为 type的类型'std :: enable_if';
'enable_if'不能用于禁用此声明

error: no type named 'type' in 'std::enable_if'; 'enable_if' cannot be used to disable this declaration

我想了解上述代码的问题以及如何解决

I want to understand what is wrong with the above code and how can it be fixed?

推荐答案

SFINAE仅适用于推导的模板参数。在您的情况下,您的方法不依赖于方法调用中的任何参数,因此它不在推论上下文中。实例化类本身时,所有信息都是已知的。

SFINAE only works for deduced template arguments. In your case your method did not depend on any parameter from the method call so it is not in a deduced context. Everything is already known while instantiate the class itself.

在这种情况下,MSVC完全是错误的。

MSVC is simply wrong in that case.

解决方法:

template<typename... Args>
class C
{
    public:
        template< typename U = std::tuple<Args...>>
            std::enable_if_t< (std::tuple_size<U>::value > 0 ) > foo(const Args&...)
            {
                std::cout << "Args  > 0 type " << std::endl;
            }

        template< typename U = std::tuple<Args...>>
            std::enable_if_t< (std::tuple_size<U>::value == 0)> foo(const Args&...)
            {
                std::cout << "Args 0 type " << std::endl;
            }
};

int main()
{
    C<>{}.foo();
    C<int>{}.foo(1);
}

我不知道为什么需要这样的重载,因为如果参数列表为空,您只需为此写一个重载而根本不包含任何SFINAE东西。

I don't know why you need such a overload, because if the parameter list is empty, you simply should write an overload for such without any SFINAE stuff at all.

如果您的编译器没有过时(仅适用于c ++ 14),则很多使用 constexpr if 更容易:

if your compiler is not outdated ( c++14 only ) it is much easier with using constexpr if:

template <typename... Args>
struct C
{
    void foo (const Args&... args)
    {
        if constexpr ( sizeof...(args) == 0)
        {
            std::cout << "0" << std::endl;
        }
        else
        {
            std::cout << ">0" << std::endl;
        }
    }
};

int main ()
{
    C<>    c0;
    C<int> c1;
    c0.foo();
    c1.foo(42);
}

评论后编辑:

为避免SFINAE,您还可以使用专门的模板类,如下所示:

To avoid SFINAE you can also use specialized template classes like this:

// provide common stuff here
template <typename ... ARGS>
class CAll { protected: void DoSomeThing(){ std::cout << "Do some thing" << std::endl; } };

template<typename ... ARGS>
class C;

// special for no args
template<>
class C<>: public CAll<>
{   
    public:
        void foo() 
        {
            std::cout << "none" << std::endl; 
            this->DoSomeThing();
        }   
};  

//special for at minimum one arg
template<typename FIRST, typename ... REST>
class C<FIRST, REST...>: public CAll<FIRST, REST...>
{   
    public:
        void foo( FIRST&, REST&... )
        {   
            std::cout << "lvalue" << std::endl;
            this->DoSomeThing();
        }

        void foo( FIRST&&, REST&&... )
        {   
            std::cout << "rvalue" << std::endl;
            this->DoSomeThing();
        }   
};  

int main()
{   
    int a;
    C<>{}.foo();
    C<int>{}.foo(1);
    C<int>{}.foo(a);
}

这篇关于为什么'enable_if'不能在此处禁用此声明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆