Why Can Template Instances Not Be Deduced in 'Std::Reference_Wrapper'S

Why can template instances not be deduced in `std::reference_wrapper`s?

Edit: Moved my guesswork to the bottom, here comes the normative text why this won't work. TL;DR version:

No conversions allowed if the function parameter contains a deduced template parameter.


§14.8.3 [temp.over] p1

[...] When a call to that name is written (explicitly, or implicitly using the operator
notation), template argument deduction (14.8.2) and checking of any explicit template arguments (14.3) are performed for each function template to find the template argument values (if any) that can be used with that function template to instantiate a function template specialization that can be invoked with the call arguments.

§14.8.2.1 [temp.deduct.call] p4

[...] [ Note: as specified in 14.8.1, implicit conversions will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter contains no template-parameters that participate in template argument deduction. [...] —end note ]

§14.8.1 [temp.arg.explicit] p6

Implicit conversions (Clause 4) will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter type contains no template-parameters that participate in template argument deduction. [ Note: Template parameters do not participate in template argument deduction if they are explicitly specified. [...] —end note ]

Since std::basic_string depends on deduced template parameters (CharT, Traits), no conversions are allowed.


This is kind of a chicken and egg problem. To deduce the template argument, it needs an actual instance of std::basic_string. To convert to the wrapped type, a conversion target is needed. That target has to be an actual type, which a class template is not. The compiler would have to test all possible instantiations of std::basic_string against the conversion operator or something like that, which is impossible.

Suppose the following minimal testcase:

#include <functional>

template<class T>
struct foo{
int value;
};

template<class T>
bool operator<(foo<T> const& lhs, foo<T> const& rhs){
return lhs.value < rhs.value;
}

// comment this out to get a deduction failure
bool operator<(foo<int> const& lhs, foo<int> const& rhs){
return lhs.value < rhs.value;
}

int main(){
foo<int> f1 = { 1 }, f2 = { 2 };
auto ref1 = std::ref(f1), ref2 = std::ref(f2);
ref1 < ref2;
}

If we don't provide the overload for an instantiation on int, the deduction fails. If we provide that overload, it's something the compiler can test against with the one allowed user-defined conversion (foo<int> const& being the conversion target). Since the conversion matches in this case, overload resolution succeeds and we got our function call.

std::reference_wrapper, constructor implementation explaination

It's a technique you can use when you want the behaviour of the "forwarding reference", U&& in this case, but at the same time restrict what can bind to it.

Deduction of T is aided by the deduction guide provided below. The detail::FUN<T>(std::declval<U>()) is there to ensure that the constructor is disabled when U is deduced to be an rvalue reference, by selecting the deleted overload and producing an invalid expression. It is also disabled if the U is another reference wrapper, in which case the copy constructor should be selected.

Here are a few examples of valid and invalid reference_wrappers:

int i = 1; 

// OK: FUN<int>(std::declval<int&>()) is valid
std::reference_wrapper<int> rwi(i);

// error: forming pointer to reference
std::reference_wrapper<int&> rwi2(i);

// OK, uses deduction guide to find T = int
std::reference_wrapper rwi3(i);
std::reference_wrapper rwi4(++i);

// error: cannot deduce T, since there is no deduction guide for
// rvalue reference to T
std::reference_wrapper rwi5(std::move(i));

// error: substitution failure of FUN<int>(int&&)
std::reference_wrapper<int> rwi6(std::move(i));
std::reference_wrapper<int> rwi7(i++);
std::reference_wrapper<int> rwi8(i + i);
std::reference_wrapper<int> rwi9(2);

As you can see, the call to the deleted FUN<T>(T&&) only comes into play in the last 4 cases: when you explicitly specify T, but attempt to construct from an rvalue.

Problem when trying to obtain an std::reference_wrapper with a particular template class parameter

You'll need a common base for the elements you store references too. This could be one way:

struct ProgressBarBase {
// Optional: A virtual destructor if you want to delete objects via
// base class pointers:
virtual ~ProgressBarBase() = default;
};

template <class bar_type>
struct ProgressBar : public ProgressBarBase {};

With that, you could change your class slightly to store references to ProgressBarBase (or whatever indicator base class you'd like).

template <class T, std::size_t count>
class MultiProgressBar {
public:
template<class... Indicators, std::enable_if_t<sizeof...(Indicators) == count, int> = 0>
MultiProgressBar(Indicators&&... bars)
: bars_{std::forward<Indicators>(bars)...}
{
// or this instead of SFINAE:
static_assert(sizeof...(Indicators) == count, "wrong number of arguments");
}

private:
std::array<std::reference_wrapper<T>, count> bars_;
};

int main() {
ProgressBar<int> prog_int;
ProgressBar<double> prog_double;
ProgressBar<float> prog_float;

MultiProgressBar<ProgressBarBase, 3> bars(prog_int, prog_double, prog_float);
}

However, if you for example have a function, like void update(bar_type value); in ProgressBar, making it virtual in the base class will not work (since bar_type is not known in the base class).

One option could be to drop the std::array and use a std::tuple instead. This makes it possible to keep the type information and it also gets rid of the need for the a base class. You also do not need the reference_wrapper since the references won't be stored in an array.

C++17 example:

template <class bar_type>
struct ProgressBar{
void update(bar_type v) {
std::cout << value << '\n';
value = v;
}

bar_type value;
};

template <class... Indicators>
class MultiProgressBar {
public:
template<class... Inds>
MultiProgressBar(Inds&&... bars) : bars_{std::forward<Inds>(bars)...} {}

void update() {
std::apply([](auto&... rw){
(rw.update(0), ...);
}, bars_);
}

private:
std::tuple<Indicators&...> bars_;
};

// deduction guide
template<class... Indicators>
MultiProgressBar(Indicators...) -> MultiProgressBar<Indicators...>;

C++17 Demo

I didn't notice the C++11 tag until I had finished the above. Here's a C++11 example too. It's a lot more to implement but I couldn't think of an easier way to do it right now:

#include <iostream>
#include <tuple>
#include <utility>

// A type to generate indices for parameter packs:
template<size_t... Is>
struct indices { };

template<size_t N, size_t... Is>
struct gen_indices : gen_indices<N - 1, N - 1, Is...> { };

template<size_t... Is>
struct gen_indices<0, Is...> : indices<Is...> { };

template <class bar_type>
struct ProgressBar{
void update(bar_type v) {
std::cout << value << '\n';
value = v;
}

bar_type value;
};

template <class... Indicators>
class MultiProgressBar {
public:
template<class... Inds>
MultiProgressBar(Inds&&... bars) : bars_{std::forward<Inds>(bars)...} {}

void update() {
// call the update overload what takes an indices<size_t...>
update(gen_indices<sizeof...(Indicators)>());
}

private:
template<size_t... Ids>
void update(indices<Ids...>) {
// call update on every element in the tuple
// , 0 is done to discard the `void` return from `update`
// to build a dummy initializer list (since C++11 lacks fold expressions)
auto dummy = { (std::get<Ids>(bars_).update(0), 0)... };
(void) dummy; // quiet warning about unused variable
}

std::tuple<Indicators&...> bars_;
};

// Since deduction guides doesn't exist in C++11, we'll add a helper function:
template<class... Indicators>
MultiProgressBar<typename std::remove_reference<Indicators>::type...>
make_MultiProgressBar(Indicators&&... inds) {
return {std::forward<Indicators>(inds)...};
}

int main() {
ProgressBar<int> prog_int{1};
ProgressBar<double> prog_double{2};
ProgressBar<float> prog_float{3};

auto bars = make_MultiProgressBar(prog_int, prog_double, prog_float);
bars.update();

// all set to 0:
std::cout << prog_int.value << prog_double.value << prog_float.value << '\n';
}

C++11 Demo

For C++11 you could make it simpler by making it possible to provide functors containing the function you want to call for all elements in the tuple:

template <class... Indicators>
class MultiProgressBar {
public:
template<class... Inds>
MultiProgressBar(Inds&&... bars) : bars_{std::forward<Inds>(bars)...} {}

static size_t size() { return sizeof...(Indicators); }

template <class Func, class... Args>
void for_one(size_t idx, Func&& func, Args&&... args) {
call_one(idx, gen_indices<sizeof...(Indicators)>(),
std::forward<Func>(func), std::forward<Args>(args)...);
}

template<class Func, class... Args>
void for_each(Func func, Args&&... args) {
// call `call_all` that takes an indices<size_t...>
// with a function and the arguments to pass to the function
call_all(gen_indices<sizeof...(Indicators)>(), func, std::forward<Args>(args)...);
}

private:
template <size_t... Ids, class Func, class... Args>
void call_one(size_t idx, indices<Ids...>, Func&& func, Args&&... args) {
[](...) {} ( // define a dummy lambda that takes any arguments and call it
// with the below as arguments. Short-circuit evaluation makes sure
// that `func` only gets called when `idx == Ids`
(idx == Ids && // (void) below to avoid warnings about unused return vals
((void)std::forward<Func>(func)(
std::get<Ids>(bars_), std::forward<Args>(args)...),
false))...
);
}

template<size_t... Ids, class Func, class... Args>
void call_all(indices<Ids...>, Func&& func, Args&&... args) {
// call `func` with every element in the tuple
// ", 0" is done to discard the `void` return from `update`
// to build a dummy initializer list (since C++11 lacks fold expressions)
auto dummy = { (func(std::get<Ids>(bars_), args...), 0)... };
(void) dummy; // quiet warning about unused variable
}

std::tuple<Indicators&...> bars_;
};

A functor could then look like this:

template< class T > // borrowed from c++20
struct type_identity { using type = T; };

struct updater { // instead of a lambda with auto as argument type
template<template<class> class PB, class bar_type>
auto operator()(PB<bar_type>& pb,
typename type_identity<bar_type>::type v) const -> decltype(pb.update(bar_type{}))
{
return pb.update(v);
}
};

and be used like this:

int main() {
ProgressBar<int> prog_int{1};
ProgressBar<double> prog_double{2};
ProgressBar<float> prog_float{3};

auto bars = make_MultiProgressBar(prog_int, prog_double, prog_float);
bars.for_each(updater{}, 4);

// all set to 4:
std::cout << prog_int.value << '\n'
<< prog_double.value << '\n'
<< prog_float.value << '\n';

for(size_t i = 0; i < bars.size(); ++i) {
bars.for_one(i, updater{}, i);
}

// 0, 1 and 2
std::cout << prog_int.value << '\n'
<< prog_double.value << '\n'
<< prog_float.value << '\n';
}

Demo

What is the crux of std::reference_wrapper implementation for the only purpose of makin std::ref work?

The logic that you're talking about is implemented entirely within std::bind itself. The main functionality it needs from std::reference_wrapper is the fact that it can be "unwrapped" (i.e., you can call .get() on it in order to retrieve the underlying reference). When the call wrapper (i.e. object returned from std::bind) is called, it simply checks whether any of its bound arguments is a std::reference_wrapper. If so, it calls .get() to unwrap it, then passes the result to the bound callable.

std::bind is complicated because it is required to support various special cases, such as recursive binding (this feature is now considered a design mistake), so instead of trying to show how to implement the full std::bind, I'll show a custom bind template that's sufficient for the example on cppreference:

template <class Callable, class... Args>
auto bind(Callable&& callable, Args&&... args) {
return [c=std::forward<Callable>(callable), ...a=std::forward<Args>(args)] () mutable {
c(detail::unwrap_reference_wrapper(a)...);
};
}

The idea is that bind saves its own copy of the callable and each of the args. If an argument is a reference_wrapper, the reference_wrapper itself will be copied, not the referent. But when the call wrapper is actually invoked, it unwraps any saved reference wrapper argument. The code to do this is simple:

namespace detail {
template <class T>
T& unwrap_reference_wrapper(T& r) { return r; }

template <class T>
T& unwrap_reference_wrapper(reference_wrapper<T>& r) { return r.get(); }
}

That is, arguments that are not reference_wrappers are simply passed through, while reference_wrappers go through the second, more specialized overload.

The reference_wrapper itself merely needs to have a relevant constructor and get() method:

template <class T>
class reference_wrapper {
public:
reference_wrapper(T& r) : p_(std::addressof(r)) {}
T& get() const { return *p_; }

private:
T* p_;
};

The ref and cref functions are easy to implement. They just call the constructor, having deduced the type:

template <class T>
auto ref(T& r) { return reference_wrapper<T>(r); }

template <class T>
auto cref(T& r) { return reference_wrapper<const T>(r); }

You can see the full example on Coliru.

(The actual constructor of std::reference_wrapper, as shown on cppreference, is complicated because it needs to satisfy the requirement that the constructor will be SFINAE-disabled if the argument would match an rvalue reference better than an lvalue reference. For the purposes of your question, it doesn't seem necessary to elaborate on this detail further.)

How to correctly use std::reference_wrappers

Class std::reference_wrapper<T> implements an implicit converting operator to T&:

operator T& () const noexcept;

and a more explicit getter:

T& get() const noexcept;

The implicit operator is called when a T (or T&) is required. For instance

void f(some_type x);
// ...
std::reference_wrapper<some_type> x;
some_type y = x; // the implicit operator is called
f(x); // the implicit operator is called and the result goes to f.

However, sometimes a T is not necessarily expected and, in this case, you must use get. This happens, mostly, in automatic type deduction contexts. For instance,

template <typename U>
g(U x);
// ...
std::reference_wrapper<some_type> x;
auto y = x; // the type of y is std::reference_wrapper<some_type>
g(x); // U = std::reference_wrapper<some_type>

To get some_type instead of std::reference_wrapper<some_type> above you should do

auto y = x.get(); // the type of y is some_type
g(x.get()); // U = some_type

Alternativelly the last line above could be replaced by g<some_type>(x);.
However, for templatized operators (e.g. ostream::operator <<()) I believe you can't explicit the type.

Best way to treat std::reference_wrapperDerived as std::reference_wrapperBase

That's just not possible:

std::vector< std::reference_wrapper< Derived > > d;
std::vector< std::reference_wrapper< Base > >& b = d;

What could happen, if this was legal?

SomeOtherDerivedClass o;
b.push_back(o); // sure, b is vector of Base, so legal

But b actually just is a reference to d, so you just managed to place a different, illegal type into d.

So even if Base is a base class of Derived, the same doesn't apply for the corresponding containers, be it std::vector or any other one.

Overloading operator== for const std::reference_wrapper in std::unordered_map

You cannot provide your own overload for operator== for non-user-defined types. That is, at best, undefined behavior. However, you don't need to do that here. std::unordered_map has five template parameters:

template<
class Key,
class T,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;

See the 4th one? That's what you want. You need to provide a function to compare. Luckily, you can use std::hash and std::equal_to like this:

std::unordered_map<
std::reference_wrapper<std::string>,
int,
std::hash<std::string>,
std::equal_to<std::string>
> stringMap;

C++ failed template argument deduction. why?

Thanks to the hint from Xeo to here about that implicit type conversions are not allowed when deducing template arguments, I wondered wether the second parameter might cause problems here. I thought that it would do the type deduction from left to right and once the type is deducted, it is not a problem anymore (for the function pointer to boost::function cast).

It seems I was wrong and this was exactly the problem.

Another version of the same thing avoids the problem:

template<typename T>
struct PartialFuncWrapper {
::Ref<Iterator<T> > i;
PartialFuncWrapper(::Ref<Iterator<T> > _i) : i(_i) {}
typename Iterator<T>::Ref operator()(boost::function<bool(T)> pred) {
return new FilterIterator<T>(i, pred);
}
};

template<typename T>
PartialFuncWrapper<T> GetFilterIterator(::Ref<Iterator<T> > i) {
return PartialFuncWrapper<T>(i);
}

Then I can write:

Ref<Iterator<CWorm*> > x = GetFilterIterator(worms())(&CWorm::getLocal);

Transform std:vectorreference_wrapperBase to std:vectorreference_wrapperDerived Runtime error time: 0 memory: 3412 signal:6

Nothing is wrong with dynamicCast(). The problem is here:

    numbers.push_back(new B(2*i));

You only ever construct instances of B which is the base class. You can't possibly cast them to D which is the derived class.

Perhaps you meant to construct derived instances and store them in your vector of references to base.



Related Topics



Leave a reply



Submit