As is, with your implementation, you cannot do this, but you can use this rule to your advantage:
Temporary objects cannot be bound to non-const references
You can move the code from the class to an freestanding function which takes a non-const reference parameter. If you do so, You will get a compiler error if an temporary tries to bind to the non-const reference.
class Foo
{
public:
Foo(const char* ){}
friend void InitMethod(Foo& obj);
};
void InitMethod(Foo& obj){}
int main()
{
Foo myVar("InitMe");
InitMethod(myVar); //Works
InitMethod("InitMe"); //Does not work
return 0;
}
Output
prog.cpp: In function ‘int main()’:
prog.cpp:13: error: invalid initialization of non-const reference of type ‘Foo&’ from a temporary of type ‘const char*’
prog.cpp:7: error: in passing argument 1 of ‘void InitMethod(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() {}")
A few years ago I wrote a patch for the GNU C++ compiler which adds a new warning option for that situation. This is tracked in a Bugzilla item.
Unfortunately, GCC Bugzilla is a burial ground where well-considered patch-included feature suggestions go to die. :)
This was motivated by the desire to catch exactly the sort of bugs that are the subject of this question in code which uses local objects as gadgets for locking and unlocking, measuring execution time and so forth.
Foo f() {
// OK (no temporary)
Foo f1("hello");
// may throw (may introduce a temporary on behalf of the compiler)
Foo f2 = "hello";
// may throw (introduces a temporary that may be optimized away
Foo f3 = Foo("hello");
// OK (no temporary)
Foo f4{"hello"};
// OK (no temporary)
Foo f = { "hello" };
// always throws
Foo("hello");
// OK (normal copy)
return f;
// may throw (may introduce a temporary on behalf of the compiler)
return "hello";
// OK (initialized temporary lives longer than its initializers)
return { "hello" };
}
int main() {
// OK (it's f that created the temporary in its body)
f();
// OK (normal copy)
Foo g1(f());
// OK (normal copy)
Foo g2 = f();
}
The case of f2, f3 and the return of "hello" may not be wanted. To prevent throwing, you can allow the source of a copy to be a temporary, by resetting the guard to now guard us instead of the source of the copy. Now you also see why we used the pointers above - it allows us to be flexible.