为什么 reference_wrapper 对内置类型的行为不同?
Why reference_wrapper behaves differently for built-in types?
我将 std::reference_wrapper
用于内置类型 (double
) 和用户定义类型 (std::string
)。
为什么它们在流运算符的情况下表现不同?
#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
为什么在第一种情况下 DR 被转换为 double 并打印出来,而在第二种情况下却没有?有解决办法吗?
好的,我现在明白了,在 ostream 的情况下,我试图调用一个未解析的模板函数:
#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
}
第一部分TC给了你答案。也就是说,basic_string 的 operator<< 是模板化的,并且模板参数推导不会查看隐式转换。
如果您不想显式 static_cast
您的引用包装器,您也可以调用 SR.get()
。
现在对于第二部分,string_fun
将 std::basic_string<C>
个对象作为输入参数。当你打电话时:
string_fun(SR);
将 SR
作为 std::reference_wrapper<std::string>
类型的输入参数,自然会出现类型不匹配。
你可以做的是提供额外的重载:
template<class C>
void string_fun(std::reference_wrapper<std::basic_string<C>> const& t) {
};
或者如果你想要更统一的处理方式,你可以定义你的 string_fun
来获取模板模板参数,并使用某种类型特征魔法解析类型,如下所示:
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;
}
我将 std::reference_wrapper
用于内置类型 (double
) 和用户定义类型 (std::string
)。
为什么它们在流运算符的情况下表现不同?
#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
为什么在第一种情况下 DR 被转换为 double 并打印出来,而在第二种情况下却没有?有解决办法吗?
好的,我现在明白了,在 ostream 的情况下,我试图调用一个未解析的模板函数:
#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
}
第一部分TC给了你答案。也就是说,basic_string 的 operator<< 是模板化的,并且模板参数推导不会查看隐式转换。
如果您不想显式 static_cast
您的引用包装器,您也可以调用 SR.get()
。
现在对于第二部分,string_fun
将 std::basic_string<C>
个对象作为输入参数。当你打电话时:
string_fun(SR);
将 SR
作为 std::reference_wrapper<std::string>
类型的输入参数,自然会出现类型不匹配。
你可以做的是提供额外的重载:
template<class C>
void string_fun(std::reference_wrapper<std::basic_string<C>> const& t) {
};
或者如果你想要更统一的处理方式,你可以定义你的 string_fun
来获取模板模板参数,并使用某种类型特征魔法解析类型,如下所示:
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;
}