通过值传递的CRTP模式的未初始化副本
unititialized copy via CRTP pattern passed by value
我正在使用CRTP模式,并尝试定义应用于其实现的操作符。我发现未初始化的对象有一个奇怪的行为
CRTP基类:
template < class C >
struct CRTP
{
using self_t = C;
const self_t& self() const
{ return static_cast<const self_t&>(*this); }
self_t& self()
{
const CRTP& cs = static_cast<const CRTP&>(*this);
return const_cast<self_t&>(cs.self());
}
void printValue()
{ cout << "CRTP value : " << self().getValue() << endl; }
};
实现1:struct Impl : public CRTP<Impl> {
Impl() = default;
Impl(Impl&&) = default;
Impl(const Impl&) = default;
explicit
Impl(int i) : v(i) { }
friend void swap(Impl& l, Impl& r)
{ using std::swap; swap(l.v, r.v); }
Impl& operator=(Impl o)
{ swap(*this, o); return *this; }
int getValue() const
{ return v; }
private:
int v;
};
实现2:template < class Arg >
struct Neg : public CRTP< Neg<Arg> >
{
Neg() = default;
Neg(Neg&&) = default;
Neg(const Neg&) = default;
explicit
Neg(Arg arg) : a(arg) { }
friend void swap(Neg& l, Neg& r)
{ using std::swap; swap(l.a, r.a); }
Neg& operator=(Neg o)
{ swap(*this, o); return *this; }
int getValue() const
{ return -a.getValue(); }
private:
Arg a;
};
操作符(operator!
工作正常,operator~
显示问题):
template < class C >
Neg<C> operator~(CRTP<C> v)
{ return Neg<C>(std::move(v.self())); }
template < class C >
Neg<C> operator-(const CRTP<C>& v)
{ return Neg<C>(v.self()); }
template < class C >
Neg<C> operator-(CRTP<C>&& v)
{ return Neg<C>(std::move(v.self())); }
现在用一个简单的main:
int main(void)
{
auto n = -Impl(10);
n.printValue();
n = ~Impl(20);
n.printValue();
}
用gcc编译,我有:
CRTP value : -10
CRTP value : 0
使用CLANG编译得到:
CRTP value : -10
CRTP value : -1186799704
现在我有两个问题:
- 这是行为标准吗?即使我删除了默认值,它也会发生构造函数。
- 如何实现运算符中的"传递值"风格"?我有n个想要实现的运算符变量模板,我不能枚举左值和右值引用的每一个组合。
你在operator ~
的参数切片你的对象。您正在按值获取CRTP<C>
,这意味着CRTP
子对象之外的任何内容(例如成员v
或a
)都将被删除。然后,您通过将类型为CRTP<C>
的切片对象强制转换为类型为C
来调用未定义行为。
按值传递时,需要按正确类型(—)的值传递对象的实际类型。对于任何C
,您的对象都不是CRTP<C>
类型,它们是从CRTP<C>
(对于某些C
)派生的类型。
如果您想保留按值传递签名,您必须接受任何内容,并使用SFINAE检查是否存在正确的基类:
template <class C>
typename std::enable_if<std::is_base_of<CRTP<C>, C>::value, Neg<C>>::type operator~ (C v)
{ return Neg<C>(std::move(v.self())); }
或者你可以使用完全转发,用同样的技巧
相关文章:
- 是否可以初始化不可复制类型的成员变量(或基类)
- 副本初始化的默认模板参数推导
- 为什么 std::string s = "123" 当不涉及副本时被视为复制初始化?
- C 标准:通过复制返回以初始化无RVO的参考:是否有任何副本
- 为什么我的类的副本初始化不适用于使用字符串文字的string_view?
- C 中副本初始化要求的放松17
- 是否可以编写便于副本初始化的显式构造函数
- 为什么初始化列表会导致2个数据副本,而不仅仅是单个副本将其传递给函数
- 构造函数初始化列表中的 C 结构副本C++
- 初始化引用时何时以及为何获得副本
- 错误的数组初始化与赋值副本构造函数初始化
- C++-初始化由不同线程拥有的对象的两个副本
- 在使用auto时初始化结构会导致VS 2013中的副本
- 从计数和元素值初始化 std::vector 与副本
- 带有单个副本的初始化std::字符串
- 在显式初始化期间尝试创建函数副本时出错
- 用函数返回值初始化std::string,是否有副本
- 通过值传递的CRTP模式的未初始化副本
- 用ref. parameter初始化ref. field:是一个副本
- 为什么在初始化列表中不需要创建额外的副本就可以对数据进行赋值