admin管理员组

文章数量:1355684

I'm trying to build the compile-time "tupled" version of std::transform. This type traits should take in a std::tuple, a unary type trait that "returns" a type (such as std::remove_pointer) and should produce a std::tuple whose types are the result of applying the type trait to the corresponding type of the input std::tuple

For example, my_tuple_trasform<std::tuple<int*, float&, double*>, std::remove_pointer>::type should be equivalent to std::tuple<int, float&, double>.

I could easily write a solution recursively, but I was trying to implement an "iterative" solution through an expansion pack, and I can't figure out what I'm getting wrong.

Here's my code:

template <typename Tuple, template<typename> typename TraitPredicate>
struct my_tuple_trasform;

template <template<typename> typename TraitPredicate, typename... Ts>
struct my_tuple_trasform<std::tuple<Ts...>, TraitPredicate>
{
    using type = std::tuple<(typename TraitPredicate<Ts>::type)...>;
};

The compiler produces an unhelpful error, where it points out that the expression

(typename TraitPredicate<Ts>::type)

is not recognized as a parameter pack and it also seems to think that the whole thing, ellipsis included, is just the first type of the std::tuple.

I'm trying to build the compile-time "tupled" version of std::transform. This type traits should take in a std::tuple, a unary type trait that "returns" a type (such as std::remove_pointer) and should produce a std::tuple whose types are the result of applying the type trait to the corresponding type of the input std::tuple

For example, my_tuple_trasform<std::tuple<int*, float&, double*>, std::remove_pointer>::type should be equivalent to std::tuple<int, float&, double>.

I could easily write a solution recursively, but I was trying to implement an "iterative" solution through an expansion pack, and I can't figure out what I'm getting wrong.

Here's my code:

template <typename Tuple, template<typename> typename TraitPredicate>
struct my_tuple_trasform;

template <template<typename> typename TraitPredicate, typename... Ts>
struct my_tuple_trasform<std::tuple<Ts...>, TraitPredicate>
{
    using type = std::tuple<(typename TraitPredicate<Ts>::type)...>;
};

The compiler produces an unhelpful error, where it points out that the expression

(typename TraitPredicate<Ts>::type)

is not recognized as a parameter pack and it also seems to think that the whole thing, ellipsis included, is just the first type of the std::tuple.

Share Improve this question edited Mar 28 at 16:36 JeJo 33.5k6 gold badges54 silver badges94 bronze badges asked Mar 28 at 13:17 Andrea BoccoAndrea Bocco 9966 silver badges16 bronze badges 1
  • 1 please include a minimal reproducible example and the compiler error message in the question – 463035818_is_not_an_ai Commented Mar 28 at 13:22
Add a comment  | 

2 Answers 2

Reset to default 6

It was close: you have to skip the parenthesis in using type = std::tuple<(typename TraitPredicate<Ts>::type)...>;

#include <tuple>

template <typename Tuple, template<typename> typename TraitPredicate>
struct my_tuple_trasform;

template <template<typename> typename TraitPredicate, typename... Ts>
struct my_tuple_trasform<std::tuple<Ts...>, TraitPredicate>
{
    using type = std::tuple<typename TraitPredicate<Ts>::type...>;
};

using T1 = std::tuple<int*, float&, double*>;
using T2 = my_tuple_trasform<T1, std::remove_pointer>::type;

static_assert (std::is_same_v<T2, std::tuple<int, float&, double>>);

int main() {}

Demo

The error was caused by wrapping the pack expansion in extra parentheses, which prevented the compiler from recognizing it as a pack expansion and made it treat the expression as a single type. This has been mentioned already in the other answer.

However, here is a slighly less verbose solution using templated lambda (require c++20 or later) with decltype:

#include <tuple>
#include <type_traits>

template <typename Tuple, template<typename> typename TraitPredicate>
using my_tuple_transform_t = decltype([]<typename... Ts>(std::tuple<Ts...>*)
    -> std::tuple<typename TraitPredicate<Ts>::type...> { return {}; }
                                      (static_cast<Tuple*>(nullptr)));

((See live demo))

Or for the case of, older compilers:

// Helper function template that is never defined, exists solely for type deduction.
template <template<typename> class TraitPredicate, typename... Ts>
auto transform_tuple_impl(std::tuple<Ts...>*) -> std::tuple<typename TraitPredicate<Ts>::type...>;

// Type alias that uses the helper function with decltype to deduce the transformed tuple type.
template <typename Tuple, template<typename> class TraitPredicate>
using my_tuple_transform_t = decltype(transform_tuple_impl<TraitPredicate>(static_cast<Tuple*>(nullptr)));


((See live demo))

本文标签: cWhy does pack expansion fail in my stdtuple transformation type traitStack Overflow