c++c++20template-meta-programmingc++-concepts

std::conditional_t evaluate false result type when condition is true


I have currently this:

template<TagType TTag, IsTag ...Tags>
struct RequiredTagList
{
    static constexpr const TagType index = TTag;
};

// ---------------------------------------------------------------

using fix::TagType = uint16_t;

template<fix::TagType Tag, IsTagList ...Ts>
struct Select_Tag_List;

template<fix::TagType Tag, IsTagList TagList, IsTagList ...TagLists>
struct Select_Tag_List<Tag, TagList, TagLists...>
{
    static constexpr const fix::TagType tag_index = TagList::index;

    using type = std::conditional_t<tag_index == Tag,
        TagList,
        typename Select_Tag_List<Tag, TagLists...>::type
    >;
};

template<fix::TagType Tag>
struct Select_Tag_List<Tag>
{
    static_assert(std::same_as<void,int>, "Tag not found, see template build stack");
};

template<fix::TagType Tag, IsTagList ...Ts>
using select_tag_list = Select_Tag_List<Tag, Ts...>;

template<fix::TagType Tag, IsTagList ...Ts>
using select_tag_list_t = typename Select_Tag_List<Tag, Ts...>::type;

The concept IsTagList validate that T is a Required<TTag, Tags...> and have member static property T::index (fix::TagType) But when I call it using this template parameter:

Select_Tag_List<268,fix::RequiredTagList<268,fix::Required<269>,fix::Required<270>,fix::Optional<271>>>

The list of template argument fix::Required<269>,fix::Required<270>,fix::Optional<271> validate the IsTag concept, but are not relevant to the issue (it could be any type/concept)

I would have assumed that std::conditional_t will not evaluate the specialization of Select_Tag_List<Tag>, but the static_assert is still trigger.

I'm using MSVC and I saw that it evaluate both the true and false result type even when not necessary, so I tried to replace std::conditional_t by this implementation, to force the non evaluation of the false value if the result is true:

template <bool Condition, typename Then, typename Else>
struct Lazy_Conditional;

template <typename Then, typename Else>
struct Lazy_Conditional<true, Then, Else>
{
    using type = Then;
};

template <typename Then, typename Else>
struct Lazy_Conditional<false, Then, Else>
{
    using type = Else;
};

template <bool Condition, typename Then, typename Else>
using lazy_conditional = Lazy_Conditional<Condition, Then, Else>;

template <bool Condition, typename Then, typename Else>
using lazy_conditional_t = typename lazy_conditional<Condition, Then, Else>::type;

But without success and still ending update with error C2039: 'type': is not a member of 'meta::Select_Tag_List<268>' and Tag not found, see template build stack

Do you have any idea on how can I make my condition not evaluate the false type if the condition result is true?


Solution

  • As @tkausl mention in a comment the ::type in the false result branch force it's evaluation:

    typename Select_Tag_List<Tag, TagsList...>::type
    

    To fix the issue I added a wrapper around the true branch as:

    using type = typename lazy_conditional_t<tag_index == Tag,
        std::type_identity<TagList>,
        Select_Tag_List<Tag, TagLists...>
    >::type;