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.)
Prevent the creation of temporary objects
Here are C++11 versions of two great answers to the same question. I don't deserve much credit for this, so consider voting for the original answers instead.
user1773602's answer
The idea is defining a deleted function with de same name as the class:
class ScopedLock {
// ...
};
void ScopedLock(...) = delete;
The usage is unusual and this might be (very?) inconvenient:
class ScopedLock a; // OK :-| but requires keyword 'class'
ScopedLock b; // Compiler error :-(
ScopedLock(); // Compiler error :-)
Johannes Schaub - litb's answer
If you don't mind having a runtime error (like the OP seems to do) rather than a compile time error then you can try this.
The basic idea is that a temporary bound to an argument of ScopedLock
's constructor will be destroyed before or after the ScopedLock
object depending on whether the later is a temporary or not.
class ScopedLock {
bool flag_ = true;
struct Flag {
bool* flag;
~Flag() {
*flag = false;
}
};
public:
ScopedLock(Flag&& f = Flag()) {
f.flag = &flag_;
}
~ScopedLock() {
if (flag_) {
std::cerr << "ScopedLock misuse\n.";
std::terminate();
}
}
};
With this, we have
ScopedLock a; // OK
ScopedLock(); // Runtime error
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
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() {}")
c++: what's the design philosophy of allowing temporary object to call non-const member function?
OK, I am putting some additional materials here. The following materials are quoted from David Vandevoorde, Nicolai M. Josuttis, Douglas Gregor-C++ Templates_ The Complete Guide-Addison-Wesley (2017), C.2.1 The Implied Argument For Member Functions.
“An old special-case permits an rvalue to be bound to an lvalue
reference to non-const type when that reference is the traditional
implicit *this parameter”
struct S{
void f1() {}//the old rule
void f2() && {}
void f3() & {}
};
int main()
{
S().f1();//Here, I THINK const this is bound to non-const, thus allowing calling non-const member functions.(the comment here is not quoted from the book.)
S().f2();
S().f3();//not okay
return 1;
}
Use -std=c++11
compilation option.
So, this means C++ designers have realized that the old rule of allowing temporary objects, which have implicit const this*
, to call non-const member function is not so good. So in C++11 they introduced &
and &&
suffixing function declaration.
But when it comes to the design philosophy of the old rule of allowing temporary objects calling non-const member function, I think it's not worth diving into this. C++ 11 has make an effort to "emend" this.(Or let programmer control this.)
Related Topics
How to Export Templated Classes from a Dll Without Explicit Specification
C++ Short-Circuiting of Booleans
Can Class Template Constructors Have a Redundant Template Parameter List in C++20
How Does _Builtin_Clear_Cache Work
Difference Between Returning Reference VS Returning Value C++
Matlab VS C++ Double Precision
Link Errors Using <Filesystem> Members in C++17
How to Truncate a File While It Is Open with Fstream
Class Type Non-Type Template Parameter Initialization Does Not Compile
Does Constraint Subsumption Only Apply to Concepts
Linking Problems Due to Symbols with Abi::Cxx11
Qt/C++ - Accessing Mainwindow UI from a Different Class
Must the Int Main() Function Return a Value in All Compilers
Get Function Pointer from Std::Function When Using Std::Bind