boost::enable_if 不在函数签名中

发布于 2024-12-24 18:44:41 字数 1616 浏览 3 评论 0 原文

这只是一个关于风格的问题:我不喜欢 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:错误:重新定义“模板 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,这是它的前身。

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. So, the idea I came up with is to put the SFINAE thing in the template arguments definition itself, like this:

#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:

../src/afg.cpp:10:97: error: redefinition of ‘template void asd()’

SFINAE there itself works, because if I delete for example the one with disable_if, the compiler error is:

../src/afg.cpp:15:12: error: no matching function for call to ‘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?

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

谎言 2024-12-31 18:44:41

嗯,我通常使用这些宏来使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”未定义之类的内容。

Well, I generally use these macros to make enable_if constructs a lot cleaner(they even work in most C++03 compilers):

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

会发光的星星闪亮亮i 2024-12-31 18:44:41

由于 C++11 使其成为可能,我只在模板参数中使用 enable_if (或相反 disable_if),就像您所做的那样。如果/当有多个重载时,我会使用虚拟的默认模板参数,这使得模板参数列表的数量不同。因此,要重用您的示例,那就是:

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

我不使用这个因为我不喜欢对参数类型进行“混乱”,就像对返回类型进行“混乱”一样,并且出于一致性原因(因为这并非在所有情况下都是可行的)。

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

南烟 2024-12-31 18:44:41

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

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

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;
}
献世佛 2024-12-31 18:44:41

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

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

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();
}
や三分注定 2024-12-31 18:44:41

那么,有没有一种方法可以不在函数的“正常”签名(即返回类型+参数列表)中实现 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[]>();
}

我看来,这种风格的可读性要高得多,并且广泛用于模板密集型库,例如 BoostSpirit 因为它倾向于编译速度更快,工作效果更好具有不太出色的模板/SFINAE 支持的编译器(例如 VC++ 和 Sun Studio)。

在线演示。

So, is there a way to accomplish SFINAE not in the "normal" signature of a function, that is return type + argument list?

Well, there's a way to obtain the same result without using SFINAE at all — overloading:

#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.Spirit because it tends to compile faster and works better with compilers having less-than-stellar template/SFINAE support (e.g. VC++ and Sun Studio).

Online demo.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文