Подтвердить что ты не робот

Трюк для того, чтобы "только" могли существовать только определенные классы?

Есть ли какой-либо трюк в С++, который может гарантировать, что пользователь класса может генерировать только rvalues?

Пример:

struct PoorClass { /* ... */};

struct EnrichedClass {
    explicit EnrichedClass (const PoorClass & poor)
        : m_poor (poor)
    {
    }

    /* additional functionality for poor objects */

private:
    const PoorClass & m_poor;
}

const EnrichedClass asEnriched (const PoorClass & poor)
{
    return EnrichedClass { poor };
}

Теперь обогащенные объекты должны быть только временными, так как они не должны пережить обернутые плохие объекты. В идеале, обогащенные объекты никогда не должны храниться в переменной, а передаваться только как значения rvalue для функций.

Есть ли способ обеспечить, чтобы, например, сделать это максимально безопасным?

4b9b3361

Ответ 1

Вы можете предоставить только методы r-значения для struct EnrichedClass:

struct EnrichedClass {
    explicit EnrichedClass (const PoorClass& poor) : m_poor (poor) {}

    /* additional functionality for poor objects */

    void foo() &&; // note the && at the end.


private:
    const PoorClass & m_poor;
};

Ответ 2

Afaik, нет способа достичь желаемой задачи напрямую.

Я могу думать об одном способе как-то "работать над проблемой":

Сделать частный или защищенный конструктор и конструктор копирования с именованной статической конструкторской функцией.

Что-то по строкам:

struct PoorClass { /* ... */ };

struct EnrichedClass
{
  EnrichedClass& operator= (const EnrichedClass & enr) = delete;
private:
  explicit EnrichedClass(const PoorClass & poor)
    : m_poor(poor)
  { }
  EnrichedClass(const EnrichedClass & enr)
    : m_poor(enr.m_poor)
  { }

public:
  static EnrichedClass enrich(const PoorClass & poor)
  {
    return EnrichedClass{ poor };
  }
  void stuff() { }
private:
  const PoorClass & m_poor;
};

Таким образом, экземпляр экземпляра нельзя копировать:

EnrichedClass::enrich(p).stuff(); // works
auto ec = EnrichedClass::enrich(p); // does not

Примечание. У всех может быть lvalues ​​ EnrichedClass через rvalue-ссылки.

void foo(EnrichedClass && e)
{
  // e = lvalue here
}

О ключевой идее rvalue-ref:

void bar() && { /* do stuff */ }

Если вы хотите передать свой rvalue функции, используя rvalue-ссылки, вы не можете вызвать функцию-член с && ref qualification, так как rvalue-ссылки все еще lvalues ​​(т.е. e не является rvalue внутри foo выше).

Вы можете сделать:

void foo(EnrichedClass && e)
{
  std::move(e).bar();
}

Но теперь вы предоставили гарантию на bar: Объект, на который ссылается this, является rvalue. Хорошо делать то, что вам нравится, пока объект находится в действительном (но неуточненном) состоянии после вызова (т.е. bar может перемещаться из this).

Таким образом, после вызова bar вы можете не знать состояние e.

Ответ 3

Похоже, вы можете сделать трюк с помощью:

  • имеют частные конструкторы в EnrichedClass
  • make asEnriched(PoorClass) вернуть EnrichedClass &&

Вот пример:

#include <iostream>

struct PoorClass {
    // trace construction and destruction
    PoorClass() {
        std::cerr << "Create " << this << std::endl;
    }
    ~PoorClass() {
        std::cerr << "Destroy " << this << std::endl;
    }
};

struct EnrichedClass {
private:
    EnrichedClass (const PoorClass & poor)
        : m_poor (poor)
    {
    }
    EnrichedClass(const EnrichedClass& rich): m_poor(rich.m_poor){}

    /* additional functionality for poor objects */

    const PoorClass & m_poor;

    friend const EnrichedClass&& asEnriched (const PoorClass & poor);
    friend void dump(const EnrichedClass& rich); // only to be able to access m_poor
};

const EnrichedClass&& asEnriched (const PoorClass & poor)
{
    return EnrichedClass ( poor ); // gives a warning, but seems harmless
          // provided the ref in only used as a parameter to a function call
          // of course don't affect it to a true ref, or do not take the address
          //  if you do not want dangling refs or pointers !
}

void dump(const EnrichedClass & rich) {
    std::cerr << "Dump " << &(rich.m_poor) << std::endl;
}

int main() {
   PoorClass poor;
   // EnrichedClass rich = poor; causes an error
   // EnrichedClass rich = asEnriched(poor); causes an e};
   dump(asEnriched(PoorClass()));
   std::cerr << "End" << std::endl;
   return 0;
}

Это отображает:

Create 0xbfbfec58
Create 0xbfbfec50
Dump 0xbfbfec50
Destroy 0xbfbfec50
end
Destroy 0xbfbfec58

означает, что ссылка действительна для продолжительности вызова дампа, в то время как невозможно изменить значение EnrichedClass на переменную.

Конечно, вы можете повлиять на ссылку. Например, const EnrichedClass& ref = asEnriched(poor); будет принят, но даст вам оборванную ссылку. Я не могу представить, как заставить компилятор обнаружить это как ошибку - ведь он уже дал предупреждение для asEnriched, возвращающего ссылку на временный...

Ответ 4

Не уверен в вопросе, не уверен в моем ответе. но я думаю, вы можете добиться этого, скрыв Poor внутри Enriched и перегрузив оператор Poor:

class Better{
    class Poor{};
    Poor& m_Poor;

public:

    Better(class Poor& poor) :m_Poor(poor){}
    operator Poor(){
        return m_Poor;
    }

};

вы не можете создать "Бедный" напрямую, поскольку он является частным. вы можете предоставить только некоторое значение r и передать Better как Poor, поскольку он имеет оператор преобразования.