如何在C++ 11,C++ 11,14和17之前简化复杂的SFINAE语法?

xin*_*aiz 12 c++ templates sfinae c++03 c++17

这个问题的灵感来自于这个答案.我想知道在给定标准中简化它的最佳方法是什么.一个我知道并且个人使用/仍在使用,因为C++ 14是宏REQUIRES(x):

定义:

template<long N>
struct requires_enum
{
    enum class type
    {
        none,
        all
    };
};

#define REQUIRES(...) requires_enum<__LINE__>::type = \
                      requires_enum<__LINE__>::type::none, \
                      bool PrivateBool = true, \
                      typename std::enable_if<PrivateBool && (__VA_ARGS__), int>::type = 0
Run Code Online (Sandbox Code Playgroud)

即使对于非模板化函数调用也使用:

template<REQUIRES(sizeof(int)==4)>
int fun() {return 0;}

int main()
{ 
    fun(); //only if sizeof(int)==4
}
Run Code Online (Sandbox Code Playgroud)

REQUIRES我使用的原文来自这篇文章.

还有什么好的技巧?


SFINAE的一些例子需要一些或很长时间才能理解读者刚开始使用SFINAE的冒险:

Pre-C++ 11 SFINAE示例(来源):

template <typename T>
struct has_typedef_foobar {
    // Types "yes" and "no" are guaranteed to have different sizes,
    // specifically sizeof(yes) == 1 and sizeof(no) == 2.
    typedef char yes[1];
    typedef char no[2];

    template <typename C>
    static yes& test(typename C::foobar*);

    template <typename>
    static no& test(...);

    // If the "sizeof" of the result of calling test<T>(nullptr) is equal to sizeof(yes),
    // the first overload worked and T has a nested type named foobar.
    static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
};
Run Code Online (Sandbox Code Playgroud)

sky*_*ack 7

如果你正在使用C++ 11(示例代码包含std::enable_if,所以我猜这是这种情况)或连续修订,我会static_assert在这种情况下使用a :

int fun() {
    static_assert(sizeof(int)==4, "!");
    return 0;
}

int main() {
    fun();
}
Run Code Online (Sandbox Code Playgroud)

您没有一套可以从中选择工作的功能.
正如我曾经说过的那样,更多的是替换失败总是错误,替换失败不是错误.
你想要的是一个编译时触发器和一个static_assert温和的错误消息.

当然,它也比复杂的sfinae表达更容易阅读!!


如果要在两个函数之间进行选择而不想使用模板机制或宏,请不要忘记重载是语言的一部分(前C++ 11工作示例):

#include <iostream>

template<bool> struct tag {};
int fun(tag<true>) { return 0; } 
int fun(tag<false>) { return 1; }
int fun() { return fun(tag<sizeof(int) == 4>()); }

int main() {
    std::cout << fun() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

这可以很容易地扩展到函数超过两个的情况:

#include <iostream>

template<int> struct tag {};
int fun(tag<0>) { return 0; }
int fun(tag<1>) { return 1; }
int fun(tag<2>) { return 2; }

int fun(bool b) {
    if(b) { return fun(tag<0>()); }
    else { return fun(tag<(sizeof(int) == 4) ? 1 : 2>());
}

int main() {
    std::cout << fun(false) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

您可以将这些函数放在匿名命名空间中,然后使用它们.


当然,还需要注意的是,在C++之前的版本11中,我们被授权为自己编写enable_if和所有其他内容type_traits.
举个例子:

template<bool b, typename = void>
struct enable_if { };

template<typename T>
struct enable_if<true, T> { typedef T type; };
Run Code Online (Sandbox Code Playgroud)