这只是一个关于样式的问题:我不喜欢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()'
SFINAE本身有效,因为如果删除例如disable_if
的那个,编译错误是:
../ src / afg.cpp:15:12:错误:没有匹配函数来调用'asd()'
这就是我想要的。
那么,有没有办法在一个函数的“正常”签名中完成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,这是它的前身。
答案 0 :(得分: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'这样的东西是未定义的。
答案 1 :(得分:8)
由于C ++ 11使它成为可能,我只在模板参数中使用enable_if
(或相反地disable_if
),就像你正在做的那样。如果/当有多个重载时,我使用虚拟的,默认的模板参数,这使得模板参数列表在arity中有所不同。所以要重用你的例子:
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;
}
另一种不弄乱返回类型(在某些情况下不可用,例如转换运算符)的替代方法是从C ++ 03开始使用默认参数:
template<typename T>
void
foo(T t, typename std::enable_if<some_trait<T>::value>::type* = nullptr);
我不使用这种形式,因为我不喜欢使用返回类型与参数类型“混乱”,并且出于一致性原因(因为在所有情况下都不可行)。
答案 2 :(得分: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;
}
答案 3 :(得分: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();
}
答案 4 :(得分: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)。