C++ std::enable_if有条件地编译成员函数

C++ std::enable_if有条件地编译成员函数,c++,templates,g++,c++11,C++,Templates,G++,C++11,我试图通过一个简单的例子来了解如何使用std::enable_if。读过之后,我觉得想出一个简单的例子应该不会太难。我想使用std::enable_if在两个成员函数之间进行选择,并且只允许使用其中一个 不幸的是,下面的代码并没有用GCC4.7编译,经过数小时的尝试,我问你们我的错误是什么 #include <utility> #include <iostream> template< class T > class Y { public:

我试图通过一个简单的例子来了解如何使用
std::enable_if
。读过之后,我觉得想出一个简单的例子应该不会太难。我想使用
std::enable_if
在两个成员函数之间进行选择,并且只允许使用其中一个

不幸的是,下面的代码并没有用GCC4.7编译,经过数小时的尝试,我问你们我的错误是什么

#include <utility>
#include <iostream>

template< class T >
class Y {

    public:
        template < typename = typename std::enable_if< true >::type >
        T foo() {
            return 10;
        }
        template < typename = typename std::enable_if< false >::type >
        T foo() {
            return 10;
        }

};


int main() {
    Y< double > y;

    std::cout << y.foo() << std::endl;
}
#包括
#包括
模板
Y类{
公众:
模板::type>
T foo(){
返回10;
}
模板::type>
T foo(){
返回10;
}
};
int main(){
YY;

std::coutSFINAE仅当模板参数的参数推导中的替换导致构造格式错误时才起作用。没有这种替换


我也想到了这一点,并尝试使用
std::is_same::value
!std::is_same::value
,得到相同的结果

这是因为当实例化类模板时(在其他情况下,当您创建类型为
Y
的对象时会发生这种情况),它会实例化其所有成员声明(不一定是它们的定义/主体!)。其中还有它的成员模板。请注意,
T
是已知的,并且
!std::is_same::value
产生false。因此它将创建一个包含

class Y<int> {
    public:
        /* instantiated from
        template < typename = typename std::enable_if< 
          std::is_same< T, int >::value >::type >
        T foo() {
            return 10;
        }
        */

        template < typename = typename std::enable_if< true >::type >
        int foo();

        /* instantiated from

        template < typename = typename std::enable_if< 
          ! std::is_same< T, int >::value >::type >
        T foo() {
            return 10;
        }
        */

        template < typename = typename std::enable_if< false >::type >
        int foo();
};
Y类{
公众:
/*实例化自
模板::value>::type>
T foo(){
返回10;
}
*/
模板::type>
int foo();
/*实例化自
模板::value>::type>
T foo(){
返回10;
}
*/
模板::type>
int foo();
};
std::enable\u if::type
访问一个不存在的类型,因此声明格式不正确。因此,您的程序无效


如果
依赖于成员模板本身的参数,则需要使成员模板的启用。\u。然后声明是有效的,因为整个类型仍然是依赖的。当您尝试调用其中一个时,会对其模板参数进行参数推导,并按预期进行SFINAE。请参阅和相应的答案关于如何做到这一点。

解决此问题的一种方法是,成员函数的专门化是将专门化放在另一个类中,然后从该类继承。您可能必须更改继承顺序才能访问所有其他底层数据,但此技术确实有效

template< class T, bool condition> struct FooImpl;
template<class T> struct FooImpl<T, true> {
T foo() { return 10; }
};

template<class T> struct FoolImpl<T,false> {
T foo() { return 5; }
};

template< class T >
class Y : public FooImpl<T, boost::is_integer<T> > // whatever your test is goes here.
{
public:
    typedef FooImpl<T, boost::is_integer<T> > inherited;

    // you will need to use "inherited::" if you want to name any of the 
    // members of those inherited classes.
};
templatestruct FooImpl;
模板结构FooImpl{
T foo(){return 10;}
};
模板结构{
T foo(){return 5;}
};
模板
Y类:public FooImpl//无论您的测试是什么,都会在这里进行。
{
公众:
typedef FooImpl继承;
//如果要命名任何
//这些继承类的成员。
};
这种技术的缺点是,如果您需要为不同的成员函数测试很多不同的东西,那么您必须为每个成员函数创建一个类,并将其链接到继承树中。这对于访问公共数据成员是正确的

例:

模板类Goo;
//重复上面的模式。
模板
Foo类:公共Goo{
公众:
typedef-Goo继承:
//等等等等。
};

我做了一个简短的例子,这个例子同样有效

#include <iostream>
#include <type_traits>

class foo;
class bar;

template<class T>
struct is_bar
{
    template<class Q = T>
    typename std::enable_if<std::is_same<Q, bar>::value, bool>::type check()
    {
        return true;
    }

    template<class Q = T>
    typename std::enable_if<!std::is_same<Q, bar>::value, bool>::type check()
    {
        return false;
    }
};

int main()
{
    is_bar<foo> foo_is_bar;
    is_bar<bar> bar_is_bar;
    if (!foo_is_bar.check() && bar_is_bar.check())
        std::cout << "It works!" << std::endl;

    return 0;
}
#包括
#包括
foo类;
分类栏;
模板
结构是
{
模板
typename std::enable_if::type check()
{
返回true;
}
模板
typename std::enable_if::value,bool>::类型检查()
{
返回false;
}
};
int main()
{
is_bar foo_is_bar;
是吧,是吧;
如果(!foo_is_bar.check()&&bar_is_bar.check())

std::cout布尔值需要依赖于推导的模板参数。因此,一种简单的修复方法是使用默认布尔值参数:

template< class T >
class Y {

    public:
        template < bool EnableBool = true, typename = typename std::enable_if<( std::is_same<T, double>::value && EnableBool )>::type >
        T foo() {
            return 10;
        }

};
您还可以实现您自己的member requires宏,如下所示(以防您不想使用其他库):

模板
结构需要枚举
{
枚举类类型
{
没有一个
全部的
};
};
#定义成员_需要(…)\
typename需要枚举::type PrivateRequestResenum 35;###行_u=需要枚举::类型::无\
class=typename std::enable\u if::type
发帖:

默认模板参数不是模板签名的一部分

但我们可以这样做:

#include <iostream>

struct Foo {
    template < class T,
               class std::enable_if < !std::is_integral<T>::value, int >::type = 0 >
    void f(const T& value)
    {
        std::cout << "Not int" << std::endl;
    }

    template<class T,
             class std::enable_if<std::is_integral<T>::value, int>::type = 0>
    void f(const T& value)
    {
        std::cout << "Int" << std::endl;
    }
};

int main()
{
    Foo foo;
    foo.f(1);
    foo.f(1.1);

    // Output:
    // Int
    // Not int
}
#包括
结构Foo{
模板::type=0>
空f(常数T和值)
{

std::cout对于那些正在寻找“只起作用”的解决方案的后来者:

跑步可以带来:

./a.out 
11

下面是我的极简主义示例,使用宏。 使用更复杂的表达式时,请使用双括号
启用_if(…)

template<bool b, std::enable_if_t<b, int> = 0>
using helper_enable_if = int;

#define enable_if(value) typename = helper_enable_if<value>

struct Test
{
     template<enable_if(false)>
     void run();
}
模板
使用helper_enable_if=int;
#定义enable_if(value)typename=helper_enable_if
结构测试
{
模板
无效运行();
}

我不确定,但我认为是这样的:如果基于SFINAE启用(替换失败不是错误)。但是,这里没有任何替换,因为没有参数不能用于确定要使用哪个重载。您应该使“true”和“false”依赖于t。(我知道你不想在这个简单的例子中这样做,但现在可能太简单了…)我也想到了这一点,并尝试使用
std::is_sametemplate<long N>
struct requires_enum
{
    enum class type
    {
        none,
        all       
    };
};


#define MEMBER_REQUIRES(...) \
typename requires_enum<__LINE__>::type PrivateRequiresEnum ## __LINE__ = requires_enum<__LINE__>::type::none, \
class=typename std::enable_if<((PrivateRequiresEnum ## __LINE__ == requires_enum<__LINE__>::type::none) && (__VA_ARGS__))>::type
#include <iostream>

struct Foo {
    template < class T,
               class std::enable_if < !std::is_integral<T>::value, int >::type = 0 >
    void f(const T& value)
    {
        std::cout << "Not int" << std::endl;
    }

    template<class T,
             class std::enable_if<std::is_integral<T>::value, int>::type = 0>
    void f(const T& value)
    {
        std::cout << "Int" << std::endl;
    }
};

int main()
{
    Foo foo;
    foo.f(1);
    foo.f(1.1);

    // Output:
    // Int
    // Not int
}
#include <utility>
#include <iostream>

template< typename T >
class Y {

    template< bool cond, typename U >
    using resolvedType  = typename std::enable_if< cond, U >::type; 

    public:
        template< typename U = T > 
        resolvedType< true, U > foo() {
            return 11;
        }
        template< typename U = T >
        resolvedType< false, U > foo() {
            return 12;
        }

};


int main() {
    Y< double > y;

    std::cout << y.foo() << std::endl;
}
g++ -std=gnu++14 test.cpp 
./a.out 
11
template<bool b, std::enable_if_t<b, int> = 0>
using helper_enable_if = int;

#define enable_if(value) typename = helper_enable_if<value>

struct Test
{
     template<enable_if(false)>
     void run();
}