【问题标题】:boost::enable_if with arithmetic expressionboost::enable_if 带算术表达式
【发布时间】:2017-03-15 10:57:19
【问题描述】:

我有一个自定义字符串类,其长度作为特化参数。长度至少应为 9 个字符,我想在编译时进行检查。

我希望为此使用 boost::enable_if,但我很难弄清楚语法。我希望有类似 boost::is_greater 的东西,类似于 boost::same_as,如下所示。但是我找不到要放入 enable_if 模板参数的表达式。有人可以帮忙吗?

很遗憾,我不能为此使用 C++11。

#include <stdio.h>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>

template<int LEN, class Enable = void>
class MyString
{
public:
    char data[LEN];
};

template<int LEN, typename boost::enable_if<boost::is_greater<LEN, 8>::type> >
class MyString
{
};

int main()
{
    MyString<4> s;
    return snprintf(s.data, 4, "123");
}

【问题讨论】:

    标签: boost enable-if


    【解决方案1】:

    boost::enable_if 允许利用SFINAE,这在某些情况下可能会有所帮助,而在其他情况下则不合适。通常,当您想根据某些条件禁用某些代码分支而不产生硬编译错误时,您希望使用 SFINAE,这通常意味着该案例将由另一个分支处理。例如,可以根据某些条件选择要调用的函数重载。

    struct A {};
    
    // This overload gets called for any type T that derives from A or for A itself
    template< typename T >
    typename boost::enable_if< typename boost::is_base_of< A, T >::type >::type
    foo(T const& t);
    
    // This overload is selected for any other types
    template< typename T >
    typename boost::disable_if< typename boost::is_base_of< A, T >::type >::type
    foo(T const& t);
    

    关于你的例子,有几点需要注意:

    • 与 C++11 中的 std::enable_if 不同,boost::enable_ifboost::disable_if 接受元函数作为其第一个模板参数。元函数是具有嵌套静态成员变量value 的类,它是元函数的结果。 Boost 还提供了boost::enable_if_cboost::disable_if_c 模板,它们和std::enable_if 一样,直接接受布尔常量。所以,如果你想检查长度是否大于8,你可以简单地写:
    typename boost::enable_if_c&lt; (LEN &gt; 8) &gt;::type

    请注意,将条件放在括号中以避免解析错误,因为更大的运算符将被解释为右尖括号。

    • 一些损坏的编译器会出现上述常量表达式的问题。对于那些编译器,可能需要将条件表示为元函数。 Boost.MPL 可以帮助您:
    typename boost::enable_if<
        boost::mpl::greater<
            boost::mpl::int_< LEN >,
            boost::mpl::int_< 8 >
        >
    >::type

    这里,boost::mpl::greater 是产生比较结果的元函数;此结果由boost::enable_if 获取。

    • 无论您使用哪种形式,在模板参数替换失败会使声明无效的上下文中使用enable_if&lt;&gt;::type 嵌套类型非常重要。对于类,这基本上相当于专业化中的模板参数列表。
    // Generic template. Used whenever none of the specializations apply.
    template< int LEN, class Enable = void >
    class MyString
    {
    };
    
    // Specialization. Used if LEN > 8 is true because
    // its second template argument (Enable, which is void by default)
    // matches the type produced by enable_if_c, which is also void
    // if the condition is true.
    template< int LEN >
    class MyString< LEN, typename boost::enable_if_c< (LEN > 8) >::type >
    {
    };

    因此,此代码有效地根据条件选择专业化。如果您希望 MyString 仅适用于满足条件的情况,您可以不定义主模板(即仅保留声明):

    // Generic template. Used whenever none of the specializations apply.
    template< int LEN, class Enable = void >
    class MyString;

    如果您真的想在违反某些编译时前提条件时仅生成硬错误,则使用静态断言可能更合适。在 C++11 中使用 static_assert 完成,在 C++03 中可以使用 Boost.StaticAssert:

    template< int LEN >
    class MyString
    {
        BOOST_STATIC_ASSERT_MSG(LEN > 8, "String length must be greater than 8");
    };

    在这种情况下,不需要专业化,并且作为额外的奖励,您将获得更好的错误消息。在 C++03 中,它会说一些关于静态断言失败的内容,指向断言行,用通俗的语言解释问题。在 C++11 中,编译器错误将包含内联消息。

    【讨论】:

    • 很好的答案,为我解决了很多问题!特别感谢您给了我一个我语无伦次地要求的 MPL 示例:幸运的是我不必使用它,但我知道它一定存在,我真的很想知道如何去做。
    猜你喜欢
    • 2011-01-25
    • 1970-01-01
    • 1970-01-01
    • 2020-01-15
    • 2023-03-20
    • 2015-10-27
    • 1970-01-01
    • 2015-07-02
    • 1970-01-01
    相关资源
    最近更新 更多