我正在研究一个简单的包装器模板类,它在调用特殊成员函数时记录。这些函数不能默认,因为它们执行其他与日志记录相关的任务。
template <typename T>
struct logger {
logger(T const& value) : value_(value) { /*...log...*/ }
logger(T&& value) : value_(std::move(value)) { /*...log...*/ }
logger(logger const& other) : value_(other.value_) { /*...log...*/ }
logger(logger&& other) : value_(std::move(other.value_)) { /*...log...*/ }
T value_;
};
不幸的是,当包装类型是 rvalue-reference 时, copy-constructor 无法使用以下错误消息进行编译:
错误:无法将'int'左值绑定到'int&amp;&amp;'
原因是对于 rvalue-reference 成员,隐式复制构造函数的行为会有所不同:
[class.copy 12.8 / 15] 非联合类
X
的隐式定义的复制/移动构造函数执行其基础和成员的成员复制/移动。 [...] 让x
成为构造函数的参数,或者对于移动构造函数,使用引用参数的xvalue。以适合其类型的方式复制/移动每个基本或非静态数据成员:
- 如果成员是一个数组,则每个元素都使用
x
的相应子对象进行直接初始化;- 如果成员m的右值引用类型为
T&&
,则会使用static_cast<T&&>(x.m)
进行直接初始化;- 否则,基础或成员使用
x
的相应基础或成员进行直接初始化。
这让我想到了一个问题:即使在使用时,如何编写一个泛型复制构造函数,其作用是一个隐式定义的复制构造函数 rvalue-references 作为成员。
对于这种特殊情况,我可以为 rvalue-references 添加一个额外的特化。但是,我正在寻找一种不限于单个成员的通用解决方案,并且不会引入代码重复。
答案 0 :(得分:3)
这里有龙。
logger(logger const& other) : value_(other.value_)
表达式other.value_
是T const
类型的左值,例如int&
,int&&
或int const
。
如果T == int&&
,您需要执行move
,因为表达式是左值。 move
相当于static_cast<int&&>
,因此您也可以直接执行static_cast
。
如果T == int&
,则不需要演员表。
如果T == int
,则不需要演员表。
对于定义为:
的副本logger(logger const& other) : value_(static_cast<T>(other.value_)) {/*...*/}
应用于第三种情况,这被定义为引入临时,并可能导致额外的复制/移动,但我认为它可以&amp;将被省略。
不依赖于复制/移动省略的解决方案是引入weird_cast
,在任何情况下都会生成所需的类型:
#include <type_traits>
template<class T, class U>
typename std::enable_if<std::is_reference<T>{}, T>::type
weird_cast(U& p)
{
return static_cast<T>(p);
}
template<class T, class U>
typename std::enable_if<not std::is_reference<T>{}, T const&>::type
weird_cast(U const& p)
{
return p;
}
int main()
{
int o = 42;
int & lo = o;
int && ro = std::move(o);
int const lco = o;
int&& r = weird_cast<int&&>(ro);
int& l = weird_cast<int& >(lo);
int d = weird_cast<int >(lco);
}
这类似于std::forward
,但也支持“转发”非引用类型。
龙在哪里?
[class.copy] / 11指定:
如果
X
有X
,则将{{1}}类的默认复制/移动构造函数定义为已删除:
- [...]
- 表示复制构造函数,rvalue引用类型
的非静态数据成员- [...]
右值引用通常绑定到xvalue或prvalue,即指向一个表达“接近其生命周期结束”的对象的表达式。由于生命周期不会通过函数边界扩展,因此容易出现这样的“复制”。
答案 1 :(得分:0)
您可以为rvalue-references写一个专门化:
template<typename T>
struct logger<T&&>{
...
};
但实际上我认为你不希望logger::_value
成为右值参考...
修改强>
虽然我认为这不是一个糟糕的解决方案,因为它是所有右值引用的GENERAL解决方法,这里是另一个没有文字特化的选项:
template<typename TT>
struct logger{
typedef typename rvalue_remover<TT>::value T;
//your previous code here
};
其中rvalue_remover
是这样的:
template<typename T>struct rvalue_remover{typedef T value;};
template<typename T>struct rvalue_remover<T&&>{typedef T value;};
我很确定这已经在c ++ 11中定义了,但我没有在这里安装它所以我不记得这个名字了。
<强> EDIT2 强>
啊!找到了!它被称为std::remove_reference<T>::type
,并在#include <type_traits>