ленивая оценка наддува eval_if - PullRequest
0 голосов
/ 12 декабря 2018

Я думаю, что у меня неправильное представление о ленивости eval_if.

Следующий код отрегулирован из хорошо известного механизма обнаружения членов, где я пытаюсь добавить функциональность, которой также должен быть членконвертируется в определенный тип.

template <typename T, typename P = size_t>
struct has_height
{
  private:
    using Yes = char[2];
    using No = char[1];

    struct Fallback
    {
        P height;
    };
    struct Derived : T, Fallback
    {
    };

    template <class U>
    static No &test(decltype(U::height) *);

    template <typename U>
    static Yes &test(U *);

  public:
    static constexpr bool has_member = sizeof(test<Derived>(nullptr)) == sizeof(Yes);

    using value_type = std::integral_constant<bool, has_member>;

    static constexpr bool value = boost::mpl::eval_if<
        value_type,
        std::is_convertible<decltype(T::height), P>,
        boost::mpl::identity<std::false_type>>::type::value;
};

template <typename T, typename P>
const bool has_height<T, P>::value;

template <typename T, typename P>
const bool has_height<T, P>::has_member;

Однако при выполнении следующего:

struct TypeA
{
     size_t width;
;

struct TypeB
{
    size_t no_width;
};
EXPECT_TRUE((has_width<TypeA, size_t>::value));
EXPECT_TRUE((has_width<TypeB, size_t>::value));

я получаю следующую ошибку:

/home/auke/git/base/sdk/src/horus/cmp/prop/reflection/has_width.hpp:49: error: ‘width’ is not a member of refl_test_reflection_types_Test::TestBody()::TypeB’
     static constexpr bool value = boost::mpl::eval_if<
                           ^~~~~

Я понимаю, что такое компиляторговорит, только не почему.Здесь я предполагаю, что, поскольку условие ложно (проверено), второй параметр (std :: is_convertible) не оценивается.

Буду признателен за любую ясность в этом вопросе, возможно, будет исправлено и возможное исправление;)

Auke

1 Ответ

0 голосов
/ 12 декабря 2018

Если вы забудете boost::mpl на секунду, и просто посмотрите на упрощенную форму этого:

eval_if<value_type, A, B>::type::value

Должно быть более ясно, что оба значения A и B оцениваются каквторой и третий параметры шаблона, которые должны быть оценены.Так что это не будет ленивым.

Если вы хотите добиться «ленивого» поведения, вы можете использовать специализацию шаблона.

template<bool, class T, class P>
struct has_height_helper {
   // `true` case. Evaluate `T::height`
   static constexpr bool value = std::is_convertible<decltype(T::height), P>::value;
}

template<class T, class P>
struct has_height_helper<false, T, P> {
    static constexpr bool value = false;
}

// And now use the helper in the class
static constexpr bool value = has_height_helper<has_member, T, P>::value;

Поскольку спецификация шаблона в основном «скрывает» код в зависимости от того, чтоиспользуется спецификация.

Другой альтернативой может быть функция constexpr с if constexpr для скрытия кода:

template<bool has_member, class T, class P>
constexpr bool has_height_helper() {
    if constexpr (has_member) {
        return std::is_convertible<decltype(T::height), P>::value;
    } else {
        return false;
    }
}

И вы можете просто перенести это в структуру:

template <typename T, typename P = size_t>
struct has_height
{
  private:
    struct Fallback
    {
        P height;
    };
    struct Derived : T, Fallback
    {
    };

    template <class U>
    constexpr static bool test(decltype(U::height) *) {
        return std::is_convertible<decltype(T::height), P>::value;
    }

    template <typename U>
    constexpr static int test(U *) {
        // Basically return any non-bool false so we can tell the difference
        return 0;
    }

  public:
    static constexpr bool has_member = std::is_same<test<Derived>(nullptr), bool>::value;

    using value_type = std::integral_constant<bool, has_member>;

    static constexpr bool value = test<Derived>(nullptr);
};
...