7

这只是一个关于风格的问题:我不喜欢 C++ 的模板元编程方式,它要求您使用返回类型或为 SFINAE 的技巧添加一个额外的虚拟参数。所以,我想出的想法是将 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[]>();
}

这个例子让 g++ 抱怨:

../src/afg.cpp:10:97: 错误: 'template void asd()' 的重新定义</p>

SFINAE 本身可以工作,因为如果我删除例如带有disable_if的那个,编译器错误是:

../src/afg.cpp:15:12: 错误:没有匹配函数调用'asd()'</p>

这就是我想要的。

那么,有没有办法在函数的“正常”签名中完成 SFINAE,即返回类型 + 参数列表?

编辑:这最终是我要在真实代码中尝试的:

#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>();
}

我使用 c++0x 的东西而不是 boost,因为只要我需要 c++0x 来使用模板参数的默认值,我认为没有理由使用 boost,它是它的前身。

4

5 回答 5

9

好吧,我通常使用这些宏来使 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

然后你会像这样定义你的函数:

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;
}

唯一的事情是,您需要在返回类型周围加上括号。如果你忘记了它们,编译器会说 'ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE' 未定义。

于 2012-02-09T23:27:06.343 回答
8

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. 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, e.g. conversion operators) that has existed since C++03 is to use default arguments:

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).

于 2012-01-05T13:37:43.547 回答
6

默认模板参数不是函数模板签名的一部分。但是模板参数的类型是。因此,您可以执行以下操作并能够重载它

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;
}
于 2012-01-05T20:00:53.540 回答
2

这可能不是您所要求的,但是好的旧模板专业化怎么样?

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();
}
于 2012-01-05T13:40:19.297 回答
2

那么,有没有办法在函数的“正常”签名中完成 SFINAE,即返回类型 + 参数列表?

好吧,有一种方法可以在完全不使用 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[]>();
}

这种风格在 IMO 上更具可读性,并且在Boost等模板繁重的库中广泛使用。Spirit,因为它倾向于编译得更快,并且与具有不太出色的模板/SFINAE 支持的编译器(例如 VC++ 和 Sun Studio)一起工作得更好。

在线演示。

于 2012-01-05T19:12:28.873 回答