Search code examples
c++variadic-templates

C++ Only accept a set of types for template parameter pack


To limit a template parameter pack to a certain type, this can be done in the following way:

    std::enable_if_t<std::conjunction_v<std::is_same<int32_t, Ts>...>>
    send(bool condition, Ts...);

I would actually allow int32_t and std::string and any order.

How can this be expressed?

I had a workaround using a variant, but this does not look so nice to me:

    using allowed_types = std::variant<int32_t, std::string>;
    template<typename... Ts>
    std::enable_if_t<std::conjunction_v<std::is_assignable<allowed_types, Ts>...>>
    send(bool condition, Ts...);

And this does not compile:

    std::enable_if_t<std::conjunction_v<std::is_same<int32_t, Ts> || std::is_same<std::string, Ts>...>> // <- does not compile!
    send(bool condition, Ts...);

Solution

  • You are using std::conjunction as a replacement for && on the std::bool_constant level correctly, but then you should in the same way replace || with std::disjunction:

    std::enable_if_t<std::conjunction_v<std::disjunction<std::is_same<int32_t, Ts>, std::is_same<std::string, Ts>>...>>
    

    or with C++17 you can use fold expressions on the level of bool values instead:

    std::enable_if_t<((std::is_same_v<int32_t, Ts> || std::is_same_v<std::string, Ts>) && ...)>>
    

    (Outer parentheses are required by the fold expression syntax.)

    Of course with C++20 this can be done much nicer with the help of concepts:

    template<typename T>
    concept Int32OrString = std::same_as<T, int32_t> || std::same_as<T, std::string>;
    
    void send(bool condition, Int32OrString auto...);
    

    or even

    template<typename T, typename... Ts>
    concept AnyOf = (std::same_as<T, Ts> || ...);
    
    void send(bool condition, AnyOf<int32_t, std::string> auto...);