How to Disallow Temporaries

How to disallow temporaries

Another macro-based solution:

#define Foo class Foo

The statement Foo("hi"); expands to class Foo("hi");, which is ill-formed; but Foo a("hi") expands to class Foo a("hi"), which is correct.

This has the advantage that it is both source- and binary-compatible with existing (correct) code. (This claim is not entirely correct - please see Johannes Schaub's Comment and ensuing discussion below: "How can you know that it is source compatible with existing code? His friend includes his header and has void f() { int Foo = 0; } which previously compiled fine and now miscompiles! Also, every line that defines a member function of class Foo fails: void class Foo::bar() {}")

Disallowing creation of the temporary objects

Edit: As j_random_hacker notes, it is possible to force the user to declare a named object in order to take out a lock.

However, even if creation of temporaries was somehow banned for your class, then the user could make a similar mistake:

// take out a lock:
if (m_multiThreaded)
{
CSingleLock c(&m_criticalSection, TRUE);
}

// do other stuff, assuming lock is held

Ultimately, the user has to understand the impact of a line of code that they write. In this case, they have to know that they're creating an object and they have to know how long it lasts.

Another likely mistake:

 CSingleLock *c = new CSingleLock(&m_criticalSection, TRUE);

// do other stuff, don't call delete on c...

Which would lead you to ask "Is there any way I can stop the user of my class from allocating it on the heap"? To which the answer would be the same.

In C++0x there will be another way to do all this, by using lambdas. Define a function:

template <class TLock, class TLockedOperation>
void WithLock(TLock *lock, const TLockedOperation &op)
{
CSingleLock c(lock, TRUE);
op();
}

That function captures the correct usage of CSingleLock. Now let users do this:

WithLock(&m_criticalSection, 
[&] {
// do stuff, lock is held in this context.
});

This is much harder for the user to screw up. The syntax looks weird at first, but [&] followed by a code block means "Define a function that takes no args, and if I refer to anything by name and it is the name of something outside (e.g. a local variable in the containing function) let me access it by non-const reference, so I can modify it.)

Can I forbid temporary objects as parameters?

If your parameter is not const, the function won't accept temporaries.

If your parameter is const, the function accepts both temporary and regular objects.

But if you want to prevent that, you can use the following

struct Object{};
void foo(const Object& o) {
/*only query o, don't alter it*/
}
void foo(Object&& ) = delete;

int main() {
Object o;
foo(o); // allow this
foo(Object{}); // but disallow this
}

Live

How to disallow temporaries in functions taking const references?

You can provide an additional overload that's a better matches for temporaries and then delete it. For example :

#include <string>

void foo(const std::string &) {}
void foo(std::string &&) = delete;

int main()
{
std::string world = "World";
foo("Hello"); // Doesn't compile, wants to use foo(std::string &&)
foo(world); // Compiles
}

How to disable instantiating a temporary class?

You seem to want to prevent users from using auto on a particular type. That's not possible in any version of C++. If it is legal C++ for a user to write T t = <expr>;, where T is the type of <expr>, then it will be legal for a user to write auto t = <expr>; (ignoring class data members). Just as you cannot forbid someone from passing <expr> to a template function using template argument deduction.

Anything you do to prevent auto usage will also inhibit some other usage of the type.



Related Topics



Leave a reply



Submit