boost :: enable_if_c<>条件参数使用短路?

时间:2015-06-25 04:33:18

标签: c++ templates boost operator-overloading sfinae

查看代码底部的is >> i?我想要g ++(C ++ 03;我有理由)使用第一个operator>>()模板 - 打印“非容器类型”的模板,因为右侧表达式是int而不是例如,vector。相反,它考虑最后一个 - 打印“固定长度容器类型”的那个。我可以从错误消息中看到它正在评估最后一个模板的enable_if_c<>条件参数,这会导致各种各样的问题,因为has_resize<T>::value会对容器不起作用。

我原以为,由于enable_if_c的条件参数is_container<C>::value中的第一个子表达式,可能估计为false,因此不会计算第二个子表达式has_resize<C>::value。分隔两者的&&运算符不会发生短路,或者int的第一个子表达式莫名其妙地评估为真。知道它是哪一个,我能做些什么呢? (调试TMP真的很难。我想在编译器考虑每个模板时逐步完成编译。)

哦,如果您将#if 1更改为#if 0,则会使用备用has_resize<T>模板,该模板可按预期工作。但是,该模板不能确定类型是否可调整大小,这正是我正在尝试做的事情。我试图上班的那个也不是一个完美的工作,但它更好。

如果你想玩代码,它也可以在Wandbox上找到。 (C++ shell也是。我正在玩在线编译器。我做了a list of them。)

#include <iostream>
#include <boost/spirit/home/support/container.hpp>

#if 1
// has_resize<T>::value is whether the (presumably) container class contains resize.
template<class T>
class has_resize
{
    struct Fallback { int resize; };
    struct Derived : T, Fallback { };

    template<class C, C>
    class check;

    typedef uint8_t no;
    typedef uint16_t yes;

    template<typename C> static no test(check<int Fallback::*, &C::resize> *);
    template<typename C> static yes test(...);

public:
    static const bool value = sizeof test<Derived>(0) == sizeof(yes);
};
#else
// has_resize<T>::value is whether the (presumably) container class contains allocator_type.
template <class T>
class has_resize
{
    typedef uint8_t yes;
    typedef uint16_t no;

    template <typename C> static yes test(class C::allocator_type *);
    template <typename C> static no test(...);

public:
    static const bool value = sizeof test<T>(0) == sizeof(yes);
};
#endif

class xstream { }; // For this example, the class doesn't need to do anything.

template <typename T>
typename boost::enable_if_c<
    !boost::spirit::traits::is_container<T>::value,
    xstream &>::type
    operator>>(xstream &ibs, T &b)
{
    std::cout << "non-container type" << std::endl;
    return ibs;
}

template <typename C>
typename boost::enable_if_c<
    boost::spirit::traits::is_container<C>::value && has_resize<C>::value,
    xstream &
>::type
operator>>(xstream &ibs, C &c)
{
    std::cout << "variable-length container type" << std::endl;
    ibs >> *c.begin();
    return ibs;
}

template <typename C>
typename boost::enable_if_c<
    boost::spirit::traits::is_container<C>::value && !has_resize<C>::value,
    xstream &
>::type
operator>>(xstream &ibs, C &c)
{
    std::cout << "fixed-length container type" << std::endl;
    ibs >> *c.begin();
    return ibs;
}

int main()
{
    int i;
    xstream is;
    is >> i;
}

更新:以下是@ Jarod42建议修复的代码:

#include <iostream>
#include <vector>
#include <set>
#if __cplusplus > 199711L
#include <array>
#endif
#include <boost/spirit/home/support/container.hpp>

#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature)               \
    template <typename U>                                                   \
    class traitsName                                                        \
    {                                                                       \
    private:                                                                \
        template<typename T, T> struct helper;                              \
        template<typename T>                                                \
        static char check(helper<signature, &funcName>*);                   \
        template<typename T> static int check(...);                         \
    public:                                                                 \
        static                                                              \
        const bool value = sizeof(check<U>(0)) == sizeof(char);             \
    }

#if __cplusplus > 199711L
DEFINE_HAS_SIGNATURE(has_resize, T::resize, void (T::*)(typename T::size_type));
#else
DEFINE_HAS_SIGNATURE(has_resize, T::resize, void (T::*)(typename T::size_type, typename T::value_type));
#endif

class xstream { }; // For this example, the class doesn't need to do anything.

template <typename T>
typename boost::enable_if_c<
    !boost::spirit::traits::is_container<T>::value,
    xstream &>::type
    operator>>(xstream &ibs, T &b)
{
    std::cout << "non-container type" << std::endl;
    return ibs;
}

template <typename C>
typename boost::enable_if_c<
    boost::spirit::traits::is_container<C>::value && has_resize<C>::value,
    xstream &
>::type
operator>>(xstream &ibs, C &c)
{
    std::cout << "variable-length container type" << std::endl;
    ibs >> *c.begin();
    return ibs;
}

template <typename C>
typename boost::enable_if_c<
    boost::spirit::traits::is_container<C>::value && !has_resize<C>::value,
    xstream &
>::type
operator>>(xstream &ibs, C &c)
{
    std::cout << "fixed-length container type" << std::endl;
    ibs >> *c.begin();
    return ibs;
}

int main()
{
    int i;
    std::vector<int> vi;
    std::set<int> si;
#if __cplusplus > 199711L
    std::array<int, 1> ai;
#endif
    xstream xs;
    xs >> i >> vi >> si;
#if __cplusplus > 199711L
    xs >> ai;
#endif
}

1 个答案:

答案 0 :(得分:1)

在您的情况下,您在struct Derived : T, Fallback { };中遇到了严重错误 与T = int ::value强制类的实例化。

我使用以下内容:

#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature)               \
    template <typename U>                                                   \
    class traitsName                                                        \
    {                                                                       \
    private:                                                                \
        template<typename T, T> struct helper;                              \
        template<typename T>                                                \
        static std::uint8_t check(helper<signature, &funcName>*);           \
        template<typename T> static std::uint16_t check(...);               \
    public:                                                                 \
        static                                                              \
        constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); \
    }

DEFINE_HAS_SIGNATURE(has_resize, T::foo, int (T::*));

Live Demo