这个问题与我的previous question有关。
我们假设我们有一个包装对象的类并通过代理返回它:
template <typename T> struct Foo
{
template <typename ... ARGS>
Foo(ARGS &&... args) : value(std::forward<ARGS>(args) ...) {}
struct Proxy
{
Proxy(T &v) : value{v} {}
T *operator->() { return &value; }
T &operator*() { return value; }
friend std::ostream &operator <<(std::ostream &o, const Proxy &p) { return o << p.value; }
private:
T &value;
};
Proxy get() { return {value}; }
private:
T value{};
};
旨在以这种方式使用:
Foo<int> integer{100};
auto integer_proxy = integer.get();
std::cout << integer_proxy << '\n'; // 100
*integer_proxy *= 2;
std::cout << integer_proxy << '\n'; // 200
我们的想法是通过调用Foo<T>::get()
创建的代理类实例访问包装对象 ,关于代理类背后的基本原理,是 protect 包装对象控制它是如何被访问的(例如,使用锁)。但是没有任何东西阻止在不使用正确的Foo<T>::Proxy
实例的情况下访问被包装的对象,例如:
Foo<int> integer{100};
std::cout << integer.get() << '\n'; // 100
*integer.get() *= 2;
std::cout << integer.get() << '\n'; // 200
在上面的代码中,Foo<T>::Proxy
对象在每个Foo<T>::get()
调用中创建并销毁,而不是将其存储在实例中并通过它进行访问。
我认为当get()
实例充当al rvalue时,重载Foo
函数可以解决问题:
template <typename T> struct Foo
{
// ...
Proxy get() { return {value}; }
Proxy get() && { throw std::logic_error{"forbiden"}; return {}; }
// ...
};
但上面的代码无法编译:
error: 'Foo<T>::Proxy Foo<T>::get() &&' cannot be overloaded Proxy get() && { throw std::logic_error{"forbidden"}; return {}; } ^~~ error: with 'Foo<T>::Proxy Foo<T>::get()' Proxy get() { return {value}; } ^~~
经过一些测试和错误后,我设法以这种方式编译代码:
template <typename T> struct Foo
{
// ...
Proxy get() & { return {value}; }
Proxy get() && { throw std::logic_error{"forbidden"}; return {}; }
// ...
};
但它也不能防止错误使用get()
功能。此外,错误在运行时引发,而编译时间是更好的选择...但是将std::logic_error
替换为static_assert(false, "forbidden")
会导致代码失败,即使静态断言函数未被调用:< / p>
Foo<int> integer{100};
std::cout << integer.get() << '\n'; // Still valid, no std::logic_error thrown
然后我尝试以与Foo<T>::Proxy::operator->()
相同的方式更改Foo<T>::get()
,但int
更改为std::vector<int>
(毕竟int
没有template <typename T> struct Foo
{
// ...
struct Proxy
{
T *operator->() & { return &value; }
T *operator->() && { throw std::logic_error{"forbidden"}; return nullptr; }
// ...
};
// ...
};
int main()
{
Foo<std::vector<int>> vector{10, 10};
auto vector_proxy = vector.get();
std::cout << vector_proxy->size() << '\n';
std::cout << vector.get()->size() << '\n'; // logic_error thrown!
return {};
}
与此运算符一起使用的成员),然后我已经实现了预期的行为:
throw
但如果我将static_assert
句更改为// logic_error thrown!
,即使get()
行被注释(因此未被调用),它也会失败;这并不能防止Foo<T>::get()
函数的错误使用。
问题:
有没有办法实现// Error
struct Err {
Proxy get() { ... }
Proxy get() && { ... }
}
// Ok
struct Ok {
Proxy get() & { ... }
Proxy get() && { ... }
}
功能的理想行为?
为什么使用左值引用重载函数会导致编译错误并且重载引用和左值引用不会?
static_assert
为什么SpecialPoint **arrayOfPointsOnTheMap = NULL;
强制在模板类成员函数体上失败会导致编译错误?我相信未使用的模板类函数没有编译(可能只适用于模板函数?)。
答案 0 :(得分:1)
只需=delete
您不想调用的重载,并且您将遇到编译时错误。
Proxy get() && = delete;
或者,在许多情况下,重载&
而不是&&
。
实际上,如果您想禁止使用临时代理,请在代理上重载&
而不是代理来源。
T *operator->()& { return &value; }
T &operator*()& { return value; }
friend std::ostream &operator <<(std::ostream &o, const Proxy&&p) = delete;
friend std::ostream &operator <<(std::ostream &o, const Proxy &p) { return o << p.value; }
仅当=delete
重载的存在允许使用rvalues并且您想要阻止它时才使用const&
。