通过代理对象访问内部对象

时间:2016-03-10 16:50:56

标签: templates c++11 operator-overloading rvalue-reference

这个问题与我的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; 强制在模板类成员函数体上失败会导致编译错误?我相信未使用的模板类函数没有编译(可能只适用于模板函数?)。

1 个答案:

答案 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&