user1180790
user1180790

Reputation:

Create foldable template parameter pack

Question

Is it possible to create foldable (※ fold expression) template parameter pack?

Example

Consider the following example (function that takes two arguments of type int (decayed)).

template<
    typename L,
    typename R,
    typename = std::enable_if_t<
            std::is_same_v<int, std::decay_t<L>>
        &&  std::is_same_v<int, std::decay_t<R>>
    >
>
int F(L Left, R Right){
    return 0x70D0;
}

Is it possible to create template parameter pack that can be folded to avoid writing same fragment of code multiple times (ie std::is_same_v)?

Something that represented as std::pack below could simplify using SFINAE?

typename = std::enable_if_t<(... && std::is_same_v<int, std::decay_t<std::pack<L, R>>>)>

What I've tried

I've tried to solve the problem using T pack and aliasing single L and R. But for some reaon the following code compiles and runs without an error (second argument of second F function call, decayed, does not equal int) on MSVC 15.9.4+28307.222:

template<
    typename... T,
    typename L = std::tuple_element_t<0, std::tuple<T...>>,
    typename R = std::tuple_element_t<1, std::tuple<T...>>,
    typename = std::enable_if_t<(... && std::is_same_v<int, std::decay_t<T>>)>
>
int F(L Left, R Right){
    return 0x70D0;
}

int main(){
    F(3, 5);   // OK
    F(3, "5"); // OK, but should not compile
}

PS Also, did I miss something in the above code to make SFINAE work properly (filter functions with int, int (decayed) arguments only)?

Upvotes: 2

Views: 216

Answers (3)

metalfox
metalfox

Reputation: 6751

You almost had it:

template <typename L,typename R,
  typename = std::enable_if_t<std::is_same_v<std::tuple<int,L,R>,std::tuple<L,R,int>>>>
int F(L Left, R Right){
    return 0x70D0;
}

int main(){
    F(3, 5);   // OK
    F(3, "5"); // Does not compile
    F("5", 3); // Does not compile
}

or the variadic version:

template <typename... T,
  typename = std::enable_if_t<std::is_same_v<std::tuple<int,T...>,std::tuple<T...,int>>>>
int F(T... args){
    return 0x70D0;
}

int main(){
    F(3, 5);   // OK
    F(3, "5"); // Does not compile
    F("5", 3); // Does not compile
}

Upvotes: 2

Vittorio Romeo
Vittorio Romeo

Reputation: 93364

Is it possible to create template parameter pack that can be folded to avoid writing same fragment of code multiple times?

In place? Not in C++17. You would have to wrap your types into some sort of template <typename...> struct typelist; and then unwrap them somewhere else. This requires one layer of indirection.

There's no way to write anything like std::pack as far as I know.


I've tried to solve the problem using T pack and aliasing single L and R. [...]

In your code, T... will always be empty as it's not being deduced by anything. L and R's default template parameter values are ignored, as they are being deduced by the function call.

You need something like:

template<
    typename... T,
    typename = std::enable_if_t<(... && std::is_same_v<int, T>)>
>
int F(T...){
    return 0x70D0;
}

In C++20, you should be able to use a lambda as follows:

template<
    typename L,
    typename R,
    typename = std::enable_if_t<[]<typename... Ts>(){
        return (... && std::is_same_v<int, Ts>)
    }.operator()<L, R>()>
>
int F(L Left, R Right){
    return 0x70D0;
}

Upvotes: 4

max66
max66

Reputation: 66230

Is too late to play ?

Is it possible to create template parameter pack that can be folded to avoid writing same fragment of code multiple times?

Not in F() itself, as far I know.

But you can repack the types, by example, in a list of a called function.

I mean... if you declare define (declare only: there isn't need of define it because is used only in a decltype()) the following function [Edit: as suggested by Barry (thanks) defining the function simplify the use]

template <typename T, typename ... Ts>
constexpr auto isSameList ()
   -> std::bool_constant<(... && std::is_same_v<T, std::decay_t<Ts>>)>
 { return {}; } 

where you can use template folding, you can SFINAE to enable/disable F() as follows

template <typename L, typename R,
          std::enable_if_t<isSameList<int, L, R>(), bool> = true>
int F(L Left, R Right)
 { return 0x70D0; }

The following is a full compiling example

#include <type_traits>

template <typename T, typename ... Ts>
constexpr auto isSameList ()
   -> std::bool_constant<(... && std::is_same_v<T, std::decay_t<Ts>>)>
 { return {}; } 

template <typename L, typename R,
          std::enable_if_t<isSameList<int, L, R>(), bool> = true>
int F(L Left, R Right)
 { return 0x70D0; }

int main ()
 {
    F(3, 5);   // compile
    //F(3, "5"); // compilation error
 }

Upvotes: 2

Related Questions