mardi 1 décembre 2015

Generating template specializations through template metaprogramming. Odd compiler behaviour

Odd compiler behaviour

Clang++

I'm getting warnings for non-deducible template arguments from Clang++, but the program outputs correctly.

Visual C++ v140

The type is detected correctly. However I get the following warning:

warning C4552: '<<': operator has no effect; expected operator with side-effect

The program does not output anything.

G++

Everything works properly.


Source code

This is basically a non-recursive std::tuple_element implementation.

Note: To make this non-recursive, you must replace std::make_index_sequence with a non-recursive implementation. I left it with std::make_index_sequence in order to provide a MVCE.

deduct<std::size_t, T> has a specialization of deduct_impl<T> that is generated from the index sequence template argument it receives. It is used in order to deduce the type at index in a variadic type template or tuple.

itp<std::size_t> and itp<std::size_t, T> is an index-type-pair used to expand the variadic indices template with a type variadic template in order to match the generated specialization.

deducer<std::size_t, T...> puts it all together by specializing deduct<std::size_t, T> and deduct_impl<T> by using std::conditional_t to generate the correct specialization.

Basically, for std::tuple<void, int, char>, in order to get the type at index 1, it creates itp_base<0>, itp<1, int>, itp_base<2> and passes it to deduct and deduct_impl.

#include <iostream>
#include <string>
#include <tuple>

template <std::size_t index>
struct itp_base {};

template <std::size_t index, typename T>
struct itp : itp_base<index> {};

template <std::size_t index, typename IndexSequence>
struct deduct;

template <std::size_t index, std::size_t... indices>
struct deduct<index, std::index_sequence<indices...>>
{
    template <typename Tuple>
    struct deduct_impl;

    template <typename T, typename... R>
    struct deduct_impl<std::tuple<itp_base<indices>..., itp<index, T>, R...>>
    {
        using type = T;
    };
};

template <std::size_t index, typename... Types>
class deducer
{
private:
    static_assert( index < sizeof...( Types ), "deducer::index out of bounds" );

    template <typename IndexSequence>
    struct deducer_impl;

    template <std::size_t... indices>
    struct deducer_impl<std::index_sequence<indices...>>
    {
        using type = typename deduct<index, std::make_index_sequence<index>
        >::template deduct_impl
        <
            std::tuple
            <
                std::conditional_t
                <
                    std::is_base_of<itp_base<indices>, itp<index, Types>>::value,
                    itp<index, Types>,
                    itp_base<indices>
                >...
            >
        >::type;
    };

public:
    using type = typename deducer_impl<
        std::make_index_sequence<sizeof...( Types )>>::type;
};

template <std::size_t index, typename... Types>
using tuple_element_t = typename deducer<index, Types...>::type;

int main()
{
    tuple_element_t<3, int, void, char, std::string> s{ "string" };
    std::cout << s << '\n';
}

Demo

http://ift.tt/1Q05Tcn

Aucun commentaire:

Enregistrer un commentaire