3

This is a long shot, however I'm trying to infer a universal reference of type std::list<T> for some T.

I have something like this:

// Is the type A the same as B, regardless of const and references
template <typename A, typename B>
struct is_same_kind {
    static constexpr auto value = std::is_same_v<std::remove_cv_t<std::remove_reference_t<A>>,
                                                 std::remove_cv_t<std::remove_reference_t<B>>>;
};

template <typename A, typename B>
static constexpr auto is_same_kind_v = is_same_kind<A,B>::value;

template<typename L, typename T, typename = std::enable_if_t<is_same_kind_v<L, std::list<T>>>>
T head(L&& l) {
   return *std::forward<L>(l).begin();
}

I get an error as the preprocessor is not able to infer T. Maybe there's some nice trick to infer both L&& as universal reference to std::list<T> and the type T from the argument l?

EDIT: Here's how to call it for example:

int main() {
    std::cout << head(std::list{1,2,3});
}

I expect to get 1.

xskxzr
  • 12,442
  • 12
  • 37
  • 77
rausted
  • 951
  • 5
  • 21

2 Answers2

1

Alright, I think I found a way to do this by defaulting to the value_type member. I'm calling remove_reference_t<L> first, as L& :: value_type won't work if it's passed by lvalue reference.

template<typename L, typename T = typename std::remove_reference_t<L>::value_type, typename = std::enable_if_t<is_same_kind_v<L, std::list<T>>>>
T head(L&& l) {
   return *std::forward<L>(l).begin();
}

int main() {
   std::cout << head(std::list{1,2,3});
}
rausted
  • 951
  • 5
  • 21
1

This is not an answer to your original question, but an important point on the implementation of your function. The way you use the captured value is incorrect. For ints it doesn't matter, because there is nothing to move, but for some types it does matter (especially for those for which L&& makes real sense). For example:

template<typename L>
auto head(L&& l) {
    return *std::forward<L>(l).begin();
}

int main() {
    std::list<std::unique_ptr<int>> list;
    list.push_back(std::make_unique<int>(1));
    list.push_back(std::make_unique<int>(2));

    auto z = head(std::move(list));
    return 0;
}

This code will not even compile. Here one is trying to make a copy of std::unique_ptr because *it is not moved from.

First, there is no much sense in calling begin() on a forwarded value. The only choice a compiler has is to call either begin() or begin() const and this choice does not depend on the value category of l. The resulting iterator is not automatically an std::move_iterator if begin() is called on an rvalue. Second, you should std::move from *it if an rvalue is passed into the function. So, the head(...) function might look like this (using some nice C++17 features):

template<typename L>
auto head(L&& l) {
    if constexpr (std::is_rvalue_reference_v<L&&>)
        return std::move(*l.begin());
    else
        return *l.begin();
}

Instead of *l.begin() we could use l.front(). The same arguments still apply.

Evg
  • 25,259
  • 5
  • 41
  • 83
  • What’s the difference between your ‘constexpr’ and ‘std::forward’? AFAIK ‘std::forward’ applied to an rvalue reference has the same effect as ‘std::move’. – rausted Aug 17 '18 at 14:28
  • 1
    @rausted, the difference is very important: you're applying `std::forward` to `l` itself (and it is a no-op in that case), but not to `*l.begin()`, as you should. `std::move(*l.begin())` and `*std::move(l).begin()` are very different things. Just try to compile my first example and you'll see. – Evg Aug 17 '18 at 14:42
  • 1
    @rausted, you can't use `std::forward` without specifying a template type parameter explicitly. If you specify the correct `T` in `std::forward`, it would work. But constructing this `T` will boil down to similar checking whether `L&&` is an rvalue reference (or whether `L` is an lvalue reference) and using `std::conditional_t`. Something like `using T = decltype(*l.begin()); using FT = std::conditional_t, std::remove_reference_t, T>; return std::forward(*l.begin());` (untested). See also: https://stackoverflow.com/a/23411192/1625187 – Evg Aug 20 '18 at 07:31