“我的SFINAE发生了什么” redux:条件模板类成员? [英] "What happened to my SFINAE" redux: conditional template class members?

查看:144
本文介绍了“我的SFINAE发生了什么” redux:条件模板类成员?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新写的模板元编程代码(而不是只读它)。所以我运行一些noob问题。其中一个很好地总结了这个非SO的帖子叫



(注意:我给了我的SFINAE发生了什么事?方法不同的名称只能帮助我的错误诊断在这个思想实验示例。请参阅 @ R.MartinhoFernandes的说明为什么您实际上不会在实际中为非重载选择此方法。)



  #include< type_traits& 

using namespace std;

template< typename T>
struct Foo {
typename enable_if< is_pointer< T> :: value,void> :: type
valid_if_pointer(T)const {}

typename disable_if< is_pointer< ; T> :: value,void> :: type
valid_if_not_pointer(T)const {}
};

int main(int argc,char * argv [])
{
int someInt = 1020;
Foo< int *>()。valid_if_pointer(& someInt);
Foo< int>().valid_if_not_pointer(304);

return 0;
}

@Alf说SFINAE发生了什么事是第一位,并给出一个建议,编译,但模板的功能,而不是类。这可能适用于某些情况,但不是全部。 (例如:我特意尝试写一个容器,它可以容纳可能是可复制构造的,也可能不是可复制构造的,我需要根据它来打开和关闭方法。)



作为解决方法,我给了这个镜头...看起来工作正常。

  #include< type_traits> 

using namespace std;

template< typename T>
struct FooPointerBase {
void valid_if_pointer(T)const {}
};

template< typename T>
struct FooNonPointerBase {
void valid_if_not_pointer(T)const {}
};

template< typename T>
struct Foo:public conditional<
is_pointer< T> :: value,
FooPointerBase< T>,
FooNonPointerBase< T> > :: type {
};

int main(int argc,char * argv [])
{
int someInt = 1020;
#if DEMONSTRATE_ERROR_CASES
Foo< int *>()。valid_if_not_pointer(& someInt);
Foo< int>()。valid_if_pointer(304);
#else
Foo< int *>()。valid_if_pointer(& someInt);
Foo< int>().valid_if_not_pointer(304);
#endif
return 0;
}

但是如果这没有断开 em>,它肯定不是一个很好的一般方法论如何打开和关闭模板类中的方法基于嗅探的traits的类型。是否有更好的解决方案?

解决方案

首先,C ++ 11 没有进行boost的 disable_if 。因此,如果您要转换升级代码,您需要使用具有否定条件的 enable_if (或重新定义您自己的 disable_if

其次,为了使SFINAE达到并适用于方法级别,这些方法必须是模板本身。但是你的测试必须针对这些模板的参数...所以代码像 enable_if< is_pointer< T> 将无法工作。你可以通过使一些模板参数(让我们说X)默认等于T,然后抛出一个静态断言,调用者没有明确地专门化它到别的东西。通过这个方法。



这意味着代替写:

  template< typename T& 
struct Foo {
typename enable_if< is_pointer< T> :: value,void> :: type
valid_if_pointer(T)const {/ * ... * /}

typename disable_if< is_pointer< T> :: value,void> :: type
valid_if_not_pointer(T)const {/ * ... * /}
}

...您会这样写:

  template< typename T> 
struct Foo {
template< typename X = T>
typename enable_if< is_pointer< X> :: value,void> :: type
valid_if_pointer(T)const {
static_assert(is_same< X,T&明确专业化);
/ * ... * /
}

template< typename X = T>
typename enable_if< not is_pointer< X> :: value,void> :: type
valid_if_not_pointer(T)const {
static_assert(is_same< X,T& t显式专门化);
/ * ... * /
}
};

两者现在都是模板, enable_if 模板参数X,而不是T,这是整个类。具体来说,在创建用于重载解析的候选集时发生的替换 - 在初始版本中,在重载解析过程中没有发生模板替换。



请注意, assert是保留原始问题的意图,并且防止某人能够编译诸如:

  Foo< int> ().valid_if_pointer< int *>(someInt); 


I'm new to writing template metaprogramming code (vs. just reading it). So I'm running afoul of some noob issues. One of which is pretty well summarized by this non-SO post called "What happened to my SFINAE?", which I will C++11-ize as this:

(Note: I gave the methods different names only to help with my error diagnosis in this "thought experiment" example. See @R.MartinhoFernandes's notes on why you wouldn't actually choose this approach in practice for non-overloads.)

#include <type_traits>

using namespace std;

template <typename T>
struct Foo {
    typename enable_if<is_pointer<T>::value, void>::type
    valid_if_pointer(T) const { }

    typename disable_if<is_pointer<T>::value, void>::type
    valid_if_not_pointer(T) const { }
};

int main(int argc, char * argv[])
{
    int someInt = 1020;
    Foo<int*>().valid_if_pointer(&someInt);    
    Foo<int>().valid_if_not_pointer(304);

    return 0;
}

@Alf says what happened to the SFINAE is "It wasn't there in the first place", and gives a suggestion that compiles, but templates the functions instead of the class. That might be right for some situations, but not all. (For instance: I'm specifically trying to write a container that can hold types that may or may not be copy-constructible, and I need to flip methods on and off based on that.)

As a workaround, I gave this a shot...which appears to work correctly.

#include <type_traits>

using namespace std;

template <typename T>
struct FooPointerBase {
    void valid_if_pointer(T) const { }
};

template <typename T>
struct FooNonPointerBase {
    void valid_if_not_pointer(T) const { }
};

template <typename T>
struct Foo : public conditional<
    is_pointer<T>::value, 
    FooPointerBase<T>,
    FooNonPointerBase<T> >::type {
};

int main(int argc, char * argv[])
{
    int someInt = 1020;
#if DEMONSTRATE_ERROR_CASES
    Foo<int*>().valid_if_not_pointer(&someInt);
    Foo<int>().valid_if_pointer(304);
#else
    Foo<int*>().valid_if_pointer(&someInt);
    Foo<int>().valid_if_not_pointer(304);
#endif
    return 0;
}

But if this is not broken (is it?), it's certainly not following a good general methodology for how to turn on and off methods in a templated class based on sniffing the type for traits. Is there a better solution?

解决方案

Firstly, C++11 did not carry forward boost's disable_if. So if you're going to transition boost code, you'll need to use enable_if with a negated condition (or redefine your own disable_if construct).

Secondly, for SFINAE to reach in and apply to the method level, those methods must be templates themselves. Yet your tests have to be done against those templates' parameters...so code like enable_if<is_pointer<T> will not work. You can finesse this by making some template argument (let's say X) default to be equal to T, and then throw in a static assertion that the caller has not explicitly specialized it to something else.

This means that instead of writing:

template <typename T>
struct Foo {
    typename enable_if<is_pointer<T>::value, void>::type
    valid_if_pointer(T) const { /* ... */ }

    typename disable_if<is_pointer<T>::value, void>::type
    valid_if_not_pointer(T) const { /* ... */ }
};

...you would write:

template <typename T>
struct Foo {
    template <typename X=T>
    typename enable_if<is_pointer<X>::value, void>::type
    valid_if_pointer(T) const {
        static_assert(is_same<X,T>::value, "can't explicitly specialize");
        /* ... */
    }

    template <typename X=T>    
    typename enable_if<not is_pointer<X>::value, void>::type
    valid_if_not_pointer(T) const {
        static_assert(is_same<X,T>::value, "can't explicitly specialize");
        /* ... */
    }
};

Both are now templates and the enable_if uses the template parameter X, rather than T which is for the whole class. It's specifically about the substitution that happens whilst creating the candidate set for overload resolution--in your initial version there's no template substitution happening during the overload resolution.

Note that the static assert is there to preserve the intent of the original problem, and prevent someone being able to compile things like:

Foo<int>().valid_if_pointer<int*>(someInt);

这篇关于“我的SFINAE发生了什么” redux:条件模板类成员?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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