Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why reference_wrapper behaves differently for built-in types?

I have the following use of std::reference_wrapper for a build in type (double) and for a user defined type (std::string).

Why do they behave differently in the case of the stream operator?

#include<functional> //reference wrapper
#include<iostream>

void fd(double& d){}
void fs(std::string& s){}

int main(){

   double D = 5.;
   std::reference_wrapper<double> DR(D);
   std::cout << "DR = " << DR << std::endl; //ok 
   fd(DR); // ok

   std::string S = "hello";
   std::reference_wrapper<std::string> SR(S);
   std::cout << "SR = " << static_cast<std::string&>(SR) << std::endl; // ok
   std::cout << "SR = " << SR << std::endl; // error: invalid operands to binary expression ('basic_ostream<char, std::char_traits<char> >' and 'std::reference_wrapper<std::string>')
   fs(SR); // ok 
}

http://coliru.stacked-crooked.com/a/fc4c614d6b7da690

Why in the first case DR is converted to double and printed and in the second it is not? Is there a work around?


Ok, I see now, in the ostream case I was trying to called a templated function that is not resolved:

#include<functional> //reference wrapper

void double_fun(double const& t){};

template<class C>
void string_fun(std::basic_string<C> const& t){};


int main(){

   double D = 5.;
   std::reference_wrapper<double> DR(D);
   double_fun(DR); //ok

   std::string S = "hello";
   std::reference_wrapper<std::string> SR(S);
   string_fun(SR); // error: no matching function for call to 'string_fun'
   string_fun(SR.get()); // ok
   string_fun(static_cast<std::string&>(SR)); // ok
   string_fun(*&SR); // would be ok if `std::reference_wrapper` was designed/coded differently, see http://stackoverflow.com/a/34144470/225186
}
like image 558
alfC Avatar asked Oct 31 '22 14:10

alfC


1 Answers

For the first part TC gave you the answer. That is, operator<< for basic_string is templated, and template argument deduction doesn't look through implicit conversions.

You could alternatively call SR.get() if you don't want to explicitly to static_cast your reference wrapper.

Now for the second part, string_fun takes as input arguments std::basic_string<C> objects. When you call:

string_fun(SR);

with SR as input parameter which is of type std::reference_wrapper<std::string>, naturally you get a type mismatch.

What you can do is provide an additional overload:

template<class C>
void string_fun(std::reference_wrapper<std::basic_string<C>> const& t) {

};

Live Demo

Or if you want a more unified treatment you could define your string_fun to take template template arguments, and resolve the type with some kind of type trait magic like bellow:

template<template<typename...> class C, typename T>
void
string_fun(C<T> const &t) {
  std::cout << 
    static_cast<std::conditional_t<
      std::is_same<
        std::reference_wrapper<T>, C<T>>::value, T, std::basic_string<T>>>(t) << std::endl;
}

Live Demo

like image 123
101010 Avatar answered Dec 22 '22 08:12

101010