Search code examples
c++templatesc++14variadic-templatestemplate-meta-programming

How to change the nth template parameter in C++?


If I am using the following type:

template<uint64_t f1 = 0, uint64_t f2 = 0, uint64_t f3 = 0, ..., uint64_t f10 = 0>
class Options;

using myOpts = Options<51, 8, 12>;

Would there be anyway I could do something equivalent to this pseudo code:

using newOpts = myOpts[6]<82>;

Which would keep all the existing template arguments and set the 7th one to 82.


Solution

  • I propose a template using as follows

    template <std::size_t Idx, std::uint64_t NewVal, typename MO>
    using NewOpts = decltype(get_new_opt<Idx, NewVal>(std::declval<MO>()));
    

    where get_new_opts() and get_new_opts_helper() are declared only helper functions as follows

    template <std::size_t Idx, std::uint64_t NewVal, std::uint64_t ... Fs,
              std::size_t ... Is>
    constexpr auto get_new_opt_helper (Options<Fs...>,
                                       std::index_sequence<Is...>)
       -> std::remove_reference_t<
             decltype( std::declval<Options<(Idx == Is ? NewVal : Fs)...>>() )>;
    
    
    template <std::size_t Idx, std::uint64_t NewVal, std::uint64_t ... Fs>
    constexpr auto get_new_opt (Options<Fs...> o)
       -> decltype( get_new_opt_helper<Idx, NewVal>
                      (o, std::make_index_sequence<sizeof...(Fs)>{}) );
    

    You can use NewOpts as follows

    using new_opt    = NewOpts<6u, 82u, myOpts>;
    

    The following is a full compiling (C++14, because uses std::index_sequence) example

    #include <cstdint>
    #include <utility>
    
    template <std::uint64_t f1 = 0u, std::uint64_t f2 = 0u, std::uint64_t f3 = 0u,
              std::uint64_t f4 = 0u, std::uint64_t f5 = 0u, std::uint64_t f6 = 0u,
              std::uint64_t f7 = 0u, std::uint64_t f8 = 0u, std::uint64_t f9 = 0u,
              std::uint64_t f10 = 0u>
    class Options
     { };
    
    template <std::size_t Idx, std::uint64_t NewVal, std::uint64_t ... Fs,
              std::size_t ... Is>
    constexpr auto get_new_opt_helper (Options<Fs...>,
                                       std::index_sequence<Is...>)
       -> std::remove_reference_t<
             decltype( std::declval<Options<(Idx == Is ? NewVal : Fs)...>>() )>;
    
    
    template <std::size_t Idx, std::uint64_t NewVal, std::uint64_t ... Fs>
    constexpr auto get_new_opt (Options<Fs...> o)
       -> decltype( get_new_opt_helper<Idx, NewVal>
                      (o, std::make_index_sequence<sizeof...(Fs)>{}) );
    
    
    template <std::size_t Idx, std::uint64_t NewVal, typename MO>
    using NewOpts = decltype(get_new_opt<Idx, NewVal>(std::declval<MO>()));
    
    int main ()
     {
       using myOpts     = Options<51u, 8u, 12u>;
       using new_opt    = NewOpts<6u, 82u, myOpts>;
       using target_opt = Options<51u, 8u, 12u, 0u, 0u, 0u, 82u>;
    
       static_assert( std::is_same<new_opt, target_opt>::value, "!" );
     }