0

Please consider the following code snippet:

template<class E>
class vector_expression {};

template<class Tuple>
class vector
    : public vector_expression<vector<Tuple>>
{
public:
    using value_type = typename Tuple::value_type;
};

template<typename T>
using dynamic_vector = vector<std::vector<T>>;

namespace detail
{
    template<class E>
    constexpr bool is_vector_expression_v = std::is_base_of_v<vector_expression<std::decay_t<E>>, std::decay_t<E>>;

    template<class E>
    struct value_type { using type = std::decay_t<E>; };
    template<class E>
    struct value_type<vector_expression<std::decay_t<E>>> { using type = typename std::decay_t<E>::value_type; };
    template<class E>
    using value_type_t = typename value_type<E>::type;
}


int main()
{
    static_assert(std::is_same<detail::value_type_t<dynamic_vector<double>>, double>::value, "not the same");   
    return 0;
}

I want value_type_t<E> to be the value_type specified in E whenever E is a vector_expression. The code above is not working, cause the template parameter E is not deducible in the partial specialization of value_type. How can I make the code work?

DEMO

0xbadf00d
  • 17,405
  • 15
  • 67
  • 107

2 Answers2

2

std::decay_t<E> is not deducible as it is in fact std::decay<E>::type (and indeed, several E can lead to same type in your specific case).

A second fix is needed to pass your static_assert:

As dynamic_vector<double> is not vector_expression but inherit from it, your specialization doesn't match. You may use SFINAE to fix that:

template<class E, typename Enabler = void>
struct value_type { using type = std::decay_t<E>; };

template<class E>
struct value_type<E, std::enable_if_t<is_vector_expression_v<E>>> {
    using type = typename std::decay_t<typename E::type>::value_type;
};

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

The first problem is that your partial specialization is non-deducible. That's an easier fix, you can just drop the std::decay_t:

template<class E>
struct value_type<vector_expression<E>> { using type = typename E::value_type; };

However, now you have the bigger problem in that this does not do what you want it to do. dynamic_vector<double> is not a vector_expression<E> for any E. It inherits from vector_expression<vector<std::vector<T>>>, but that won't help for the purposes of this match. So the above fix will compile, but you'd still match the primary template - giving you the wrong value_type.

What you probably want is to specialize on the presence of value_type as a typedef. That is:

template <class... > using void_t = void;
template <class T> struct tag_t { using type = T; }; // courtesy of Yakk

template<class E, class=void>
struct value_type : tag_t<std::decay_t<E>> { };

template<class E>
struct value_type<E, void_t<typename std::decay_t<E>::value_type>> 
: tag_t<typename std::decay_t<E>::value_type> { };

And now your static_assert passes.

Barry
  • 286,269
  • 29
  • 621
  • 977