C++: Can I make an assignment operator "explicit" - c++

I am on the task to migrate the concept of error handling in a C++ class library. Methods that previously simply returned bool (success/fail) shall be modified to return a Result object which conveys a machine readable error code and a human readable explanation (and some more which does not matter here).
Walking through thousands of lines of code is error prone, therefore I try to get the best support from the compiler for this task.
My result class has - among other member methods - a constructor that constructs the result from a code and an assignment operator for the code:
class Result
{
public:
typedef unsigned long ResultCode;
explicit Result(ResultCode code); // (1)
Result& operator=(ResultCode code); // (2)
};
Remark: I would usually use an enum class for ResultCode which would solve my problems, but this is not an option. This is because the major design objective was to use Result in different libraries, each of which shall define its own set of result codes without requiring one big header file that defines all possible result codes for all libraries. In fact, each class shall be able to define local result codes so that the list of possible result codes can be obtained from the classes header. Thus the codes cannot be enumerated in Result, they must be defined by the classes using the Result class.
To avoid implicit conversions on
return true;
Statements in the client code, the constructor has been declared explicit. But in nesting method calls, another problem occurs. Say, I have a method
bool doSomething()
{
return true;
}
Which I am using in a function that returns a Result object. I want to forward result codes of nested calls
Result doSomethingElse
{
Result result = doSomething();
return result;
}
With the current implementation of Result's assignment operator, this is not going to give me a compiler error - the boolean return value of doSomething() is implicitly converted to unsigned long.
As I have read in the C++ documentation, only constructors and conversion operators may be declared explicit.
My questions
Why is explicit not allowed for assignment operators or other methods? IMO it would make a lot of sense to allow any method to be explicit as well.
Are there other solutions to prevent implicit type conversion for the assignment operator?

Your problem isn't in the class Result: you are explicitly creating a new instance of it, after all; explicit cannot forbid it.
I don't think you can forbid the implicit promotion bool -> long.
You can work around it. One way is to make ResultCode not be an integer type. then, it could have an explicit constructor. Something like
class ResultCode
{
unsigned long m_code;
public:
explicit ResultCode(unsigned long code) : m_code(code) {}
operator unsigned long () { return m_code; }
};
would allow you to use ResultCode anywhere you can use a unsigned int and create it as ResultCode res = 5 or return ResultCode(5) but not call a function expecting a ResultCode (such as the Result constructor!) with anything which is not a ResultCode already, nor do something like return 5 if the function must return a ReturnCode.
Otherwise you can use template overloadng to 'catch' anything not being an unsigned int and force it to be an error
typedef unsigned long ResultCode;
class Result
{
ResultCode m_par;
public:
template<typename T>
Result(T param) { static_assert(false); }
template<>
Result(ResultCode par): m_par(par) {}
};
int main()
{
ResultCode a = 5; //ok
//unsigned long a = 6; //also ok
//bool a = true; //error!
//int a = 7; //also error!!
Result b(a);
}

With the current implementation of Result's assignment operator, this is not going to give me a compiler error - the boolean return value of doSomething() is implicitly converted to unsigned long.
With respect to the code you posted; it does result in an error error: no viable conversion from 'bool' to 'Result', see here.
A minimal example showing the behaviour you see in the code would be required. There is likely other constructors or type with conversion in the actual code that have a material effect on your code.
On the explicitly asked questions...
Why is explicit not allowed for assignment operators or other methods?
explicit is only allowed where implicit conversion can take place, i.e. where the compiler would attempt to generate the conversion for you (there is a special case for bool). Such conversions are constructors and the conversion (or casting operators).
Marking the constructor or conversion operator as explicit prevents the compiler from making the conversions, hence, if you require the conversion, you need to be explicit about it - as a general motivation for why this is done, it makes the code more explicit in what it does. There is a trade-off, so judicious use should be applied in both cases. The general advice is to favour explicit when in doubt.
For example;
struct Result {
Result(long src); // can be marked explicit
operator long() const; // can be marked explicit
};
Are there other solutions to prevent implicit type conversion for the assignment operator?
The assignment operator has a particular for Result& operator=(Result&);. In the assignment itself, there are no conversion. To prevent the implicit creation of a Result for the assignment, the constructor(s) need to be marked explicit.
To prevent the Result from being created from a ResultCode, you can either not declare the method, or mark it as deleted;
Result& operator=(ResultCode code) = delete;

Related

Why are different conversion functions for int and const int allowed?

Why is the following allowed to be compiled in C++?
#include<iostream>
using namespace std;
class mytest
{
public:
operator int()
{
return 10;
}
operator const int()
{
return 5;
}
};
int main()
{
mytest mt;
//int x = mt; //ERROR ambigious
//const int x = mt; //ERROR ambigious
}
Why does it make sense to allow different versions (based on constness) of the conversion operator to be compiled when their use always results in ambiguity?
Can someone clarify what I am missing here?
For conversion they're ambiguous; but you might call them explicitly. e.g.
int x = mt.operator int();
const int x = mt.operator const int();
I believe that in the strictest sense, even if it doesn't really make much sense for const, this is legitimate.
There is a difference between a function declaration and a function type, and they do not have the same constraints.
Function declarations may not differ in only their return type or (since C++17) exception specification. However, no such thing is said about the function type (to my knowledge).
The standard [class.conv.fct] decribes conversion functions as having such-and-such form (three alternatives listed), all of which do not look like normal function declarations, in particular they very obviously have no return type.
It does state, that the function type is "function taking no parameter returning conversion-type-id", but nowhere is it mentioned that conversion function declarations have any such thing as a return type. On the contrary, the three alternative forms listed very clearly do not have a return type.
Since conversion functions don't have a return type (... in their declaration), it cannot conflict. So, I guess, in the strictest, most pedantic sense, it's even kinda "legal", whether it makes sense or not.
If you think about it, then it somehow has to be legal, too. A class may very well have more than one conversion function to different things (not just differing by const). Such code does exist, and it sometimes makes a lot of sense going that way.
You might for example have a class File that converts to either a string (the filename) or to a handle_t (the operating system handle) in case you want to use some OS-specific or exotic function that your wrapper class doesn't directly support (think writev, tee, or epoll?) It's certainly something you'd expect to work!
If, however, we treated conversion functions as "just functions", then they'd only differ in their return type, which would render the declarations illegal. So... that wouldn't work.
why does it make sense to allow different version(based on constness) of conversion operator (to be compiled) when their use always result in ambiguity;
It usually makes no sense (apart from highly artificial use cases) and a compiler could warn you about it:
prog.cc:12:25: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
operator const int()
^
I come to the conclusion, that it's not explicitly allowed to write conversation operators that only differ by constness of their return value. It is too expensive for the compilation process to explicitly disallow it.
Remember that (member) functions that only differ by their return type
class mytest
{
int f();
const int f();
};
are forbidden:
error: ‘const int mytest::f()’ cannot be overloaded
It's just that conversion operators start with operator that makes the difference.

What happens internally as and when the second statement in this code is executed?

In the following C++ code, I am creating a class and declaring a private variable in that class.
#include <iostream>
class num{
int k;
public:
operator int(){
return k;
}
};
int main(){
num obj;
std::cout<<obj; // calls int()
return 0;
}
On execution, this code will print the value of 'k' in 'obj' of type num. That clearly indicates that the member function of the class num has been invoked somehow. The invoked member function has the header 'operator int()', so, how does this function get invoked since I have not operated on obj and just printed it's value ?
The compiler looks for a valid overload of
operator<<(std::ostream&, something);
Since
operator<<(std::ostream&, num const&);
is not defined/provided, it looks for any allowed alternatives. Since num allows implicit type conversion to int, this is used, creating code equivalent to
std::cout<<static_cast<int>(num);
In fact, this is the exact reason for providing such an type-conversion operator: the type (num in this case) can be used instead of the conversion type in (almost) any function call w/o the need for explicit type conversion.
The code
operator int(){
return k;
}
is a conversion of a num object to an int object. The conversion is implicit which means that it can be done automatically, i.e. even when you don't ask for it. In other words - if the compiler needs an int object but has a num object, the compiler will automatically call int() to convert num to an int.
Therefore you can do strange things like:
int n = 10 + obj;
and
void foo(int x)
{
....
}
foo(obj);
This goes even further... If the compiler needs a << but num doesn't provide one, the compiler sees that it can convert num to int and that int has a <<. Consequently the compiler decides to do that.
A personal opnion:
It might seem smart to be able to convert some type to another type automatically. However, it is also confusing - especially to your co-workers. Therefore I'll recommend to avoid implicit conversions. If you need a conversion make it explicity. Then there will be no nasty surprises.

How does constructor conversion work in C++?

How does constructor conversion work?
#include <iostream>
using namespace::std;
class One {
public:
One() { cout<<"One"<<endl;}
};
class Two {
public:
Two(const One&) {cout<<"Two(const One&)"<<endl;}
};
void f(Two) {cout<<"f(Two)"<<endl;}
int main() {
One one;
f(one);
}
produces the output
One
Two(const One&)
f(Two)
Any constructor that can be called with a single argument is considered an implicit conversion constructor. This includes simple 1-argument cases, and usage of default arguments.
This conversion is considered in any context that wants X and provided Y, and Y has such implicit conversion possibility. Note that a plenty of other, built-in conversions also play as a mix (like adjusting const-ness, integral and fp promotions, conversions, etc.) The rule is that at most one "user defined" implicit conversion is allowed in the mix.
In some cases it may be quite surprising, so the general advice is to make any such ctors explicit. That keyword makes the conversion possible but not implicitly: you must use T() syntax to force it.
As an example consider std::vector that has a ctor taking size_t, setting the initial size. It is explicit -- otherwise your foo(vector<double> const& ) function could be mistakenly called with foo(42).
It's right result. Since constructor is not explicit - implicit conversion works (here One is implicitly converted to Two).
one is created, then when passed to f converted to Two.
What the Two(const One&) {cout<<"Two(const One&)"<<endl;} constructor means is that you're allowed to construct a Two value at any time - implicitly - from a One. When you call f(one) it wants a Two parameter, it's given a One, so the compiler puts 2 and 2 together and says "I'll make a temporary Two from the One and complete the call to f()"... everyone will be happy. Hurray!
Compiler has to create copy of Two instance on stack. When you call f() with argument which is object of class One (or any other) compiler looks to definition of class Two and tries to find constructor which takes One(or any other) object(or reference) as an argument. When such constructor has been found it constructs object using it. It's called implicit because compiler do it without your interference.
class Foo {
public:
Foo(int number) {cout<<"Foo(int number)"<<endl;}
};
void f(Foo) {cout<<"f(Foo)"<<endl;}
int main() {
f(24);
} ///:~
Output will be:
Foo(int number)
f(Foo)

Can any class object be passed as test expression for any test expression such as if, while like ifstream object.

In C++, can I use my objects as test expression like ifstream objects. If not, why?
e.g.
ifstream ifs ("myfile.txt");
while( ifs ){
//read and process data..
}
I have a class, which operator do I need to overload to let compiler allow my object to be passed as test expression?
e.g.
MyClass obj1;
if( obj1 ){
//do something..
}
while( obj1 ){
//do something repeatedly..
}
Both should be valid expressions.
You have to implement a bool overload in your class. Something like this:
class myClass {
public:
explicit operator bool() const { return condition; }
};
It will work both in if and while statements. However, if your compiler does not support C++11 you can't use the explicit keyword in this overload.
There are several options you have. Probably best one is to overload the operator bool(). Like so:
class A{
public:
operator bool()
{
return flag;
}
private:
bool flag;
};
EDIT: as pointed out in the comments if you use C++11 it is better to make the operator explicit by adding the explicit keyword to the front. Otherwise probably it is better to use the operator void*()
There are a number of options.
You don’t have to implement an operator bool overload in your class.
And it’s generally not the best choice.
Best: named state checking.
The best is to use a named state checking method. For example, iostreams have the fail member, so that you can write
while( !cin.fail() ) { ... }
For your own class it can look like this:
struct S
{
bool is_good() const { return ...; } // Whatever name.
};
So-so: explicit conversion to bool.
Next best is a explicit conversion operator. Having it explicit prevents if from being called inadvertently for passing one of your objects as a function argument. An explicit conversion operator is still used in a condition, so you can write e.g.
while( cin ) { ... }
which in C++11 invokes an
explicit operator bool () const { return !fail(); }
For your own class it can look like
struct S
{
explicit operator bool () const { return ...; }
};
Ungood: implicit conversion to "private" pointer type.
Third, if you're using a compiler that does not support explicit conversions, i.e. a C++03 compiler, and if for some inexplicable reason you do not want the named checking which is the best choice, then you can choose a result type that minimizes the chance of an inadvertent call.
In C++03 iostreams used an implicit conversion to void* (instead of to bool).
Some people advocate using the "safe bool idiom" where the result is a pointer to an, in C++03, type that's inaccessible to client code.
Absolutely worst: implicit conversion to bool.
The worst option of all is like
struct S
{
operator bool () { return ... }
};
With this
One cannot see from calling code what condition is being checked.
The operator can be inadvertently called for passing an S as function argument.
The conversion can not be called on a const object.
Adding a const only makes it slightly less bad.
It’s still very bad. :-)
It is operator bool() you need to overload to provide this behaviour. But please only do this if there is a sensible semantic meaning to the conversion, that is obvious and expected by all users of the class!
Your compiler will attempt to implicit cast the expression to bool, so you will need to add a typecast operator to your class, like this:
class SomeClass {
operator bool() {
return true; // a boolean expression should go here.
}
}
this will allow for your class to be casted to a boolean type and therefore let it be used in if, while etc...
It is however important to note that this allows implicit conversions from your type to bool and it is important to make sure that this makes sense.
Often it is more sensible to provide a method for the behavior, such as:
while (myObj.hasMoreElements())
or
if (someObj.isValid())
This makes it immediately clear what is being tested. However, if a conversion to bool makes sense, go for it.
You can overload any number of type conversion operators; the
traditional one was operator void*()() const, to return a null
pointer for false, and a non-null pointer (tradiionally this)
for true. In C++11, you can also overload explicit operator
bool() const, but this is not recommended if your compiler
doesn't yet allow explicit; the fact that bool is an
integral type, and that without the explicit, it will convert
to any other integral type, can lead to some surprising overload
resolutions .
If you do this, you should also overload operator!() const, so
that if ( ! myObj ) is well defined as well.
And finally, you should really reflect as to whether you want to
do this. The ostream classes get away with it because they're
part of the standard, and everyone sees, uses and knows them,
and the while ( someStream >> object ) idiom is ubiquitious.
But on the whole, it's misleading and an abuse of operator
overloading for any class which has more than two possible
states; an isValid or isReady or whatever function is more
appropriate.

Why does 'unspecified_bool' for classes which have intrinsic conversions to their wrappered type fail?

I have recently read the safe bool idiom article. I had seen this technique used a few times, but had never understood quite why it works, or exactly why it was necessary (probably like many, I get the gist of it: simply using operator bool () const allowed some implicit type conversion shenanigans, but the details were for me always a bit hazy).
Having read this article, and then looked at a few of its implementations in boost's shared_ptr.hpp, I thought I had a handle on it. But when I went to implement it for some of the classes that we've borrowed and extended or developed over time to help manage working with Windows APIs, I found that my naive implementation fails to work properly (the source compiles, but the usage generates a compile-time error of no valid conversion found).
Boost's implementations are littered with conditions for various compilers level of support for C++. From using the naive operator bool () const, to using a pointer to member function, to using a pointer to member data. From what I gather, pointer to member data is the most efficient for compilers to handle IFF they handle it at all.
I'm using MS VS 2008 (MSVC++9). And below is a couple of implementations I've tried. Each of them results in Ambiguous user-defined-conversion or no operator found.
template<typename HandlePolicy>
class AutoHandleTemplate
{
public :
typedef typename HandlePolicy::handle_t handle_t;
typedef AutoHandleTemplate<HandlePolicy> this_type;
{details omitted}
handle_t get() const { return m_handle; }
operator handle_t () const { return m_handle; }
#if defined(NAIVE)
// The naive implementation does compile (and run) successfully
operator bool () const { return m_handle != HandlePolicy::InvalidHandleValue(); }
bool operator ! () const { return m_handle == HandlePolicy::InvalidHandleValue(); }
#elif defined(FUNC_PTR)
// handle intrinsic conversion to testable bool using unspecified_bool technique
typedef handle_t (this_type::*unspecified_bool_type)() const;
operator unspecified_bool_type() const // never throws
{
return m_handle != HandlePolicy::InvalidHandleValue() ? &this_type::get() : NULL;
}
#elif defined(DATA_PTR)
typedef handle_t this_type::*unspecified_bool_type;
operator unspecified_bool_type() const // never throws
{
return m_handle != HandlePolicy::InvalidHandleValue() ? &this_type::m_handle : NULL;
}
#endif
private :
handle_t m_handle;
{details omitted}
};
And here's a snippet of code that either works (naive implementation), or errors (either of the unspecified_bool techniques, above):
// hModule is an AutoHandleTemplate<ModuleHandlePolicy>
if (!hModule)
and:
if (hModule)
I have already tried enabling the operator! in all cases - but although the first case then works, the second fails to compile (ambiguous).
This class seems to me to be so very like a smart_ptr (or auto_ptr). It should support implicit conversion to its underlying handle type (HMODULE) in this case, but it should also handle if (instance) and if (!instance). But if I define both the operator handle_t and the unspecified_bool technique, I get errors.
Can someone please explain to me why that is so, and perhaps suggest a better approach? (or should I be content with the naive approach, at least until C++0x is complete and explicit operators are implemented in my compiler)?
EDIT:
It seems that the answer may well be that if I define an implicit conversion to an integral, that C++ will use that conversion for any if (instance) type expressions. And that, at least for the above class, the only reason to define any other operators (operator bool) is to explicitly override using the implicit integral conversion to something else (in the above case, forcing it to be a comparison to INVALID_HANDLE_VALUE instead of the implicit NULL).
And using the unspecified_bool technique only really makes sense when you're not providing an integral conversion operator?
AutoHandleTemplate<ModuleHandlePolicy> hModule( ... );
HMODULE raw_handle = hModule; // if we want to this line works,
// AutoHandleTemplate<ModuleHandlePolicy> should \
// be implicitly converted to it's raw handle type - HMODULE.
If one smart-ptr can implicitly converted to it's raw handle type and the raw handle type could be used in a boolean test itself, like :
HMODULE the_raw_handle = ...;
if ( the_raw_handle ) {} // this line is ok
For those smart-ptrs, there is no need (and should not) to define conversions to bool,void* or safe_bool, otherwise, ambiguity.
operator bool(), void*(), safe_bool() are used for the smart-ptrs which could not be implicitly convert to it's raw handle or it's raw handle couldn't used in a boolean context.
Try this code :
template<typename HandlePolicy>
class AutoHandleTemplate
{
public :
typedef typename HandlePolicy::handle_t handle_t;
typedef AutoHandleTemplate<HandlePolicy> this_type;
{details omitted}
operator handle_t () const {
return m_handle==HandlePolicy::InvalidHandleValue()? 0: m_handle;
}
// no more conversion functions
private :
handle_t m_handle;
{details omitted}
};
The ambiguity comes from having two possible conversion operators; either:
operator handle_t () const;
operator unspecified_bool_type() const;
or:
operator handle_t () const;
operator bool () const;
Both can be used in a boolean expression, so you have ambiguity.
All the idioms suck, really.
The best solution is:
1) don't have any implicit conversion operators
2) have an operator! override with bool return type. Yes, this means that some test might need to be written as if(!!myObject), but that's a small price to pay.