Hounddog
Hounddog

Reputation: 415

Allow l-value references and disallow r-value references as a function parameter?

So I have a class that manages a resource. Similar to std::mutex, it has acquire and release methods. To be a good RAII-style programmer, I want to implement the analog to std::unique_lock, to prevent the resource from from being acquired forever. However, for semantic reasons, acquire and release are const functions (trust me on this one).

So that means that my constructor for my RAII class has a signature of RAIIType( const T &). This problem is, is that an rvalue will bind to this as well. I was hoping to pick SO's brain on a way to disallow this from happening.

In coding terms:

class ResourceType
{
public:
   void acquire() const{}
   void release() const{}
};

template< class T >
class RAIIClass
{
public:
   RAIIClass(const T & in_t) : t(in_t) { t.acquire(); }
   ~RAIIClass() { t.release(); }
private:
   const T & t;
};

ResourceType foo() { return ResourceType(); }

int main()
{
   ResourceType x1;
   const ResourceType & x2(x1);

   {
      RAIIClass<ResourceType> x(x1); //Allowable
   }
   {
      RAIIClass<ResourceType> x(x2); //Allowable
   }
   {
      RAIIClass<ResourceType> x(foo()); //Currently allowable, would like to disallow.
   }
}

Any ideas?

Upvotes: 3

Views: 304

Answers (2)

Casey
Casey

Reputation: 42594

An alternative to forbidding the creation of managers for temporaries is to change the manager class to store the managed object internally when invoked with a temporary (Demo at Coliru):

template< class T >
class RAIIClass;
template <typename T>
RAIIClass<T> make_guard(T&&);

template< class T >
class RAIIClass
{
public:
   ~RAIIClass() { t.release(); }

private:
   friend RAIIClass make_guard<>(T&&);
   RAIIClass(T&& in_t) : t(std::forward<T>(in_t)) { t.acquire(); }

   T t;
};

template <typename T>
RAIIClass<T> make_guard(T&& t)  {
   return {std::forward<T>(t)};
}

ResourceType foo() { return {}; }

int main()
{
   ResourceType x1;
   const ResourceType & x2(x1);

   {
      auto x = make_guard(x1); //Allowable
   }
   {
      auto x = make_guard(x2); //Allowable
   }
   {
      auto x = make_guard(foo()); //Allowable too.
   }
}

Upvotes: 2

Howard Hinnant
Howard Hinnant

Reputation: 219588

Add this constructor:

RAIIClass(const T&&) = delete;

This will bind to either const or non-const rvalues, where as both const and non-const lvalues will prefer your existing constructor:

RAIIClass(const T & in_t) : t(in_t) { t.acquire(); }

Upvotes: 8

Related Questions