Return 将初始化列表与 returns 引用的用户定义的转换运算符一起使用时复制值
Return value is copied when using initializer list with a user-defined conversion operator that returns reference
我正在尝试围绕 shared_ptr
编写一个可以隐式取消引用基础类型的包装器。代码如下:
#include <memory>
template<typename T>
class PtrWrapper {
public:
PtrWrapper(std::shared_ptr<T> ptr) : ptr_(ptr) {}
operator T& () {
return *ptr_;
}
T& ref() {
return *ptr_;
}
private:
std::shared_ptr<T> ptr_;
};
看起来没什么问题。我尝试了几种使用包装器的方法:
#include <iostream>
class Nothing {
public:
Nothing() {
std::cout << "Construct " << this << std::endl;
}
Nothing(Nothing const& parent) {
std::cout << "Copy " << &parent << " " << this << std::endl;
}
Nothing(Nothing && parent) {
std::cout << "Move " << &parent << " " << this << std::endl;
}
~Nothing() {
std::cout << "Destruct " << this << std::endl;
}
};
int main() {
PtrWrapper<Nothing> wrapper{std::make_shared<Nothing>()};
// #1: OK
Nothing & by_assignment = wrapper;
// #2: OK
Nothing & by_operator{wrapper.operator Nothing &()};
// #3: OK
Nothing & by_function{wrapper.ref()};
// #4: OK
Nothing & by_initialization(wrapper);
// #5: Compile error: non-const lvalue reference to type 'Nothing' cannot bind to an initializer list temporary
// Nothing & by_initialization_2{wrapper};
// #6: The `Nothing` class is copied, which is not expected
Nothing const& by_initialization_3{wrapper};
return 0;
}
包装器 class 适用于赋值和括号初始化。
奇怪的是,当我尝试使用初始化列表(上面代码中的#5 和#6)初始化 Nothing&
时,该值被复制,我必须使用 const 引用它.但是,当我显式调用 wrapper.operator Nothing &()
之类的转换运算符时(上面代码中的#2),我得到了对在第一行构造的原始对象的正确引用。
我读过 cppreference 并发现初始化列表是一个复制初始化的临时列表,但是当 operator Nothing &()
被显式调用时为什么代码工作是没有意义的。
任何人都可以帮我弄清楚这里发生了什么?非常感谢!
你实际上是在reference initialization这里:
Nothing & by_initialization_2{wrapper};
规则说由于初始化器与绑定的引用不是同一类型,因此考虑 user-defined 转换运算符,这很好,因为您有适当的转换运算符。
但是,如果转换函数返回的 l-value 通过 brace-init 列表传递,则会具体化一个临时对象。由于无法将 non-const 引用绑定到临时对象,因此初始化失败。
我正在尝试围绕 shared_ptr
编写一个可以隐式取消引用基础类型的包装器。代码如下:
#include <memory>
template<typename T>
class PtrWrapper {
public:
PtrWrapper(std::shared_ptr<T> ptr) : ptr_(ptr) {}
operator T& () {
return *ptr_;
}
T& ref() {
return *ptr_;
}
private:
std::shared_ptr<T> ptr_;
};
看起来没什么问题。我尝试了几种使用包装器的方法:
#include <iostream>
class Nothing {
public:
Nothing() {
std::cout << "Construct " << this << std::endl;
}
Nothing(Nothing const& parent) {
std::cout << "Copy " << &parent << " " << this << std::endl;
}
Nothing(Nothing && parent) {
std::cout << "Move " << &parent << " " << this << std::endl;
}
~Nothing() {
std::cout << "Destruct " << this << std::endl;
}
};
int main() {
PtrWrapper<Nothing> wrapper{std::make_shared<Nothing>()};
// #1: OK
Nothing & by_assignment = wrapper;
// #2: OK
Nothing & by_operator{wrapper.operator Nothing &()};
// #3: OK
Nothing & by_function{wrapper.ref()};
// #4: OK
Nothing & by_initialization(wrapper);
// #5: Compile error: non-const lvalue reference to type 'Nothing' cannot bind to an initializer list temporary
// Nothing & by_initialization_2{wrapper};
// #6: The `Nothing` class is copied, which is not expected
Nothing const& by_initialization_3{wrapper};
return 0;
}
包装器 class 适用于赋值和括号初始化。
奇怪的是,当我尝试使用初始化列表(上面代码中的#5 和#6)初始化 Nothing&
时,该值被复制,我必须使用 const 引用它.但是,当我显式调用 wrapper.operator Nothing &()
之类的转换运算符时(上面代码中的#2),我得到了对在第一行构造的原始对象的正确引用。
我读过 cppreference 并发现初始化列表是一个复制初始化的临时列表,但是当 operator Nothing &()
被显式调用时为什么代码工作是没有意义的。
任何人都可以帮我弄清楚这里发生了什么?非常感谢!
你实际上是在reference initialization这里:
Nothing & by_initialization_2{wrapper};
规则说由于初始化器与绑定的引用不是同一类型,因此考虑 user-defined 转换运算符,这很好,因为您有适当的转换运算符。
但是,如果转换函数返回的 l-value 通过 brace-init 列表传递,则会具体化一个临时对象。由于无法将 non-const 引用绑定到临时对象,因此初始化失败。