简体   繁体   English

boost :: enable_if不在函数签名中

[英]boost::enable_if not in function signature

This is just a question about style: I don't like the way of C++ for template metaprogramming that requires you to use the return type or add an extra dummy argument for the tricks with SFINAE. 这只是关于样式的问题:我不喜欢C ++用于模板元编程的方式,该方式要求您使用返回类型或为SFINAE的技巧添加额外的哑元参数。 So, the idea I came up with is to put the SFINAE thing in the template arguments definition itself, like this: 因此,我想到的想法是将SFINAE东西放在模板参数定义本身中,如下所示:

#include <iostream>
#include <boost/type_traits/is_array.hpp>
#include <boost/utility/enable_if.hpp>
using namespace std;

template <typename T, typename B=typename boost::enable_if< boost::is_array<T> >::type > void asd(){
    cout<<"This is for arrays"<<endl;
}

template <typename T, typename B=typename boost::disable_if< boost::is_array<T> >::type > void asd(){
    cout<<"This is for NON arrays"<<endl;
}

int main() {
    asd<int>();
    asd<int[]>();
}

This example make g++ complain: 这个例子使g ++抱怨:

../src/afg.cpp:10:97: error: redefinition of 'template void asd()' ../src/afg.cpp:10:97:错误:“模板无效asd()”的重新定义

SFINAE there itself works, because if I delete for example the one with disable_if , the compiler error is: SFINAE本身可以工作,因为例如如果我删除带有disable_if那个,则编译器错误是:

../src/afg.cpp:15:12: error: no matching function for call to 'asd()' ../src/afg.cpp:15:12:错误:没有匹配的函数可调用“ asd()”

Which is what I want. 这就是我想要的。

So, is there a way to accomplish SFINAE not in the "normal" signature of a function, that is return type + argument list? 因此,有没有一种方法可以不在函数的“正常”签名(即返回类型+参数列表)中完成SFINAE?

EDIT: This is in the end what I'm going to try in the real code: 编辑:这是最后我要在真实代码中尝试的方法:

#include <iostream>
#include <type_traits>
using namespace std;

template <typename T, typename enable_if< is_array<T>::value, int >::type =0 > void asd(){
    cout<<"This is for arrays"<<endl;
}

template <typename T, typename enable_if< !is_array<T>::value, int >::type =0 > void asd(){
    cout<<"This is for NON arrays"<<endl;
}

int main() {
    asd<int[]>();
    asd<int>();
}

I use c++0x stuff instead of boost because as long as I need c++0x for using defaults of template arguments, I see no reason to use boost, which is its precursor. 我使用c ++ 0x而不是boost,因为只要我需要c ++ 0x来使用模板参数的默认值,我就没有理由使用boost了,boost是它的先驱。

Well, I generally use these macros to make enable_if constructs a lot cleaner(they even work in most C++03 compilers): 好吧,我通常使用这些宏来使enable_if构造更整洁(它们甚至可以在大多数C ++ 03编译器中使用):

#define ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE(...) __VA_ARGS__>::type
#define FUNCTION_REQUIRES(...) typename boost::enable_if<boost::mpl::and_<__VA_ARGS__, boost::mpl::bool_<true> >, ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE
#define EXCLUDE(...) typename boost::mpl::not_<typename boost::mpl::or_<__VA_ARGS__, boost::mpl::bool_<false> >::type >::type

Then you would define your function like this: 然后,您将如下定义函数:

template <typename T >
FUNCTION_REQUIRES(is_array<T>)
(void) asd(){
    cout<<"This is for arrays"<<endl;
}

template <typename T >
FUNCTION_REQUIRES(EXCLUDE(is_array<T>))
(void) asd(){
    cout<<"This is for NON arrays"<<endl;
}

The only thing is, you need to put parenthesis around the return type. 唯一的是,您需要在返回类型两边加上括号。 If you forget them, the compiler will say something like 'ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE' is undefined. 如果您忘记了它们,则编译器会说“ ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE”是未定义的。

Since C++11 made it possible, I only ever use enable_if (or conversely disable_if ) inside the template arguments, the way you're doing. 由于C ++ 11使其成为可能,所以我只在模板参数内使用enable_if (或相反的disable_if ),这是您的操作方式。 If/when there are several overloads, then I use dummy, defaulted template arguments which makes the template parameter lists differ in arity. 如果/当有多个重载时,我将使用默认的虚拟模板参数,这会使模板参数列表有所不同。 So to reuse your example that would be: 因此,重用您的示例将是:

template<
    typename T
    , typename B = typename boost::enable_if<
        boost::is_array<T>
    >::type
>
void asd() {
    cout << "This is for arrays" << endl;
}

template<
    typename T
    , typename B = typename boost::disable_if<
        boost::is_array<T>
    >::type
    , typename = void
>
void asd() {
    cout << "This is for arrays" << endl;
}

Another alternative to not messing the return type (that is not available in some cases, eg conversion operators) that has existed since C++03 is to use default arguments: 自从C ++ 03起不存在的不弄乱返回类型(在某些情况下,例如转换运算符不可用)的另一种替代方法是使用默认参数:

template<typename T>
void
foo(T t, typename std::enable_if<some_trait<T>::value>::type* = nullptr);

I don't use this form as I dislike 'messing' with the argument types just as much as with the return type, and for consistency reasons (since that's not doable in all cases). 我不使用这种形式,因为出于一致性的原因(因为并非在所有情况下都不可行),我不喜欢将参数类型与返回类型一样“弄乱”。

Default template arguments are not part of the signature of function templates. 默认模板参数不属于功能模板的签名。 But the type of template parameters is. 但是模板参数的类型是。 So you can do the following and be able to overload it 因此,您可以执行以下操作并能够使其过载

template <
  typename T,
  typename boost::enable_if< 
    boost::is_array<T>, int 
  >::type = 0
> 
void asd() {
    cout<<"This is for arrays"<<endl;
}

template <
  typename T, 
  typename boost::disable_if< 
    boost::is_array<T>, int 
  >::type = 0 
>
void asd() {
    cout<<"This is for arrays"<<endl;
}

This may not exactly be what you're asking for, but how about good old template specialization? 这可能不完全是您要的,但是好的旧模板专业化又如何呢?

template<typename T>
struct asd
{
    static void fgh()
    {
        std::cout << "not an array\n";
    }
};

template<typename T>
struct asd<T[]>
{
    static void fgh()
    {
        std::cout << "an array of unknown size\n";
    }
};

template<typename T, size_t N>
struct asd<T[N]>
{
    static void fgh()
    {
        std::cout << "an array of known size\n";
    }
};

int main()
{
    asd<int>::fgh();
    asd<int[]>::fgh();
    asd<int[42]>::fgh();
}

So, is there a way to accomplish SFINAE not in the "normal" signature of a function, that is return type + argument list? 因此,有没有一种方法可以不在函数的“正常”签名(即返回类型+参数列表)中完成SFINAE?

Well, there's a way to obtain the same result without using SFINAE at all — overloading: 好吧,有一种方法可以完全不使用SFINAE来获得相同的结果-重载:

#include <iostream>
#include <type_traits>

void asd_impl(std::true_type&&)
{
    std::cout << "This is for arrays\n";
}

void asd_impl(std::false_type&&)
{
    std::cout << "This is not for arrays\n";
}

template<typename T>
void asd()
{
    asd_impl(std::is_array<T>());
}

int main()
{
    asd<int>();
    asd<int[]>();
}

This style is far more readable IMO, and is used extensively in template-heavy libraries such as Boost . 这种样式在IMO中更具可读性,并且在大量使用模板的库(例如Boost)中广泛使用。 Spirit because it tends to compile faster and works better with compilers having less-than-stellar template/SFINAE support (eg VC++ and Sun Studio). 精神,因为它倾向于更快地编译并且可以更好地与模板/ SFINAE支持不那么出色的编译器(例如VC ++和Sun Studio)一起工作。

Online demo. 在线演示。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM