Consider the following class, just as a simple example:
#include <iostream>
#include <string>
using namespace std;
class point {
public:
int _x{ 0 };
int _y{ 0 };
point() {}
point(int x, int y) : _x{ x }, _y{ y } {}
operator string() const
{ return '[' + to_string(_x) + ',' + to_string(_y) + ']'; }
friend ostream& operator<<(ostream& os, const point& p) {
// Which one? Why?
os << static_cast<string>(p); // Option 1
os << p.operator string(); // Option 2
return os;
}
};
Should one call a conversion operator directly, or rather just call static_cast and let that do the job?
Those two lines will pretty much do exactly the same thing (which is to call the conversion operator), there's no real difference between their behavior as far as I can tell. So the real question here is whether that's true or not. Even though these seem the same to me, there could still be subtle differences that one might fail to pick up on.
So are there any practical differences between those approaches (including ones that might not apply to this example), other than the fact that the syntax for them different? Which one should be preferred and why?
So are there any practical differences between those approaches
In this case, not that I know of, behaviour wise.
(including ones that might not apply to this example)
static_cast<X>(instance_of_Y) would also allow conversion if X has a converting constructor for the type Y. An explicit call to (possibly non-existent) conversion operator of Y could not use the mentioned converting constructor. In this case of course, std::string does not have a converting constructor for point.
So, the cast is more generic and that is what I would prefer in general. Also "convert this object to type string" is more meaningful than "call the operator string()". But if for some very strange reason you want to avoid the converting constructor, then explicit call to conversion operator would achieve that.
No you never need to call the conversion operator member function directly.
If you use an instance of the class where a std::string object is expected then the conversion operator will be called automatically by the compiler, as will it if you use e.g. static_cast to cast an instance to std::string.
Simple and stupid example:
void print_string(std::string const& s)
{
std::cout << s << '\n';
}
int main()
{
point p(1, 2);
print_string(p); // Will call the conversion operator
print_string(static_cast<std::string>(p)); // Will call the conversion operator too
}
The closest to call the function directly you will ever need is using something like static_cast.
In your specific case, with the output operator, then you should use static_cast. The reason is semantic and for future readers and maintainers (which might include yourself) of the code.
It will of course work to call the conversion member function directly (your option 2) but it loses the semantic information that says "here I'm converting the object to a string".
If the only use of the conversion operator is to use in the output operator, you might as well create a (private) function that takes the output stream reference as an argument, and writes directly to the stream, and call that function from the output operator.
Related
I understand the value of keyword explicit when used in cases where there is a chance of creating ambiguity, like the examples I am seeing here and here.
Which I understand as prevents implicit conversion of basic types to object type, and this makes sense.
struct point {explicit point(int x, int y = 0);
};
point p2 = 10; // Error: would be OK without explicit
What I want to understand is what value explicit brings when I am using custom datatypes as constructor argument?
struct X {X(int x);}; // a sample custom datatype I am referring to.
struct pointA { pointA(X x);}; // here this looks to me same as
struct pointB {explicit pointB(X x);}; // like here this
int main() {
pointA pa = 10; // fails as expected
return 0;
}
The point of explicit has nothing to do with the parameter list, it has to do with the type being constructed, especially if there are side-effects. For example, consider std::ofstream.
void foo(std::ofstream out_file);
// ...
foo("some_file.txt");
Without explicit, this will attempt to open some_file.txt and overwrite its contents. Maybe this is what you want, but it's a pretty big side-effect that's not obvious at the calling point. However, consider what we'd have to do since the relevant std::ofstream constructor is explicit.
foo(std::ofstream("some_file.txt"));
It's far more clear, and the caller isn't surprised (or at least shouldn't be).
Whether the type being constructed is built-in, from a third-party library, or something you wrote yourself: it's irrelevant. explicit is useful anytime you don't want a type to be constructed without somebody being very explicit (hence the keyword) that's their intention.
The value that explicit brings when custom data types are used as constructor argument is the same as the value when basic types are used as the argument.
That really should be the end of the answer, as trying to draw a distinction between these cases is inventing a complication that does not exist. However, the OP does not accept this and has declined to clarify why this is even a question. So to give this answer content, I will show why the question fails to demonstrate a distinction. Then I will return to the nominal question and demonstrate one way in which explicit has value, regardless of the type of the constructor's argument.
Implicit conversion sequences
The question shows a case where constructing a pointA object from an int fails, but this failure has nothing to do with whether the conversion is explicit or implicit. It cannot, since both pointA and X have implicit constructors. There is no way for the explicit specifier to come into play. The second code block in the question has no value in the context of the question. Rather than jumping to the conclusion that explicit has no value, the OP should have questioned why the conversion failed. The OP asked the wrong question, but we are stuck with it.
What the code in the question demonstrates is that an implicit conversion can use at most one user-defined conversion. Converting an int to an X is one user-defined conversion. Converting an X to a pointA is a second user-defined conversion. You cannot use both in a single implicit conversion. Hence, the failure of the question's pointA pa = 10;.
A more appropriate code example
To get a situation comparable to the first code block in the question, one should initialize with values of type X instead of those of type int. Since the first code block uses literals, let's start by looking at that case. (This is a little more complicated than the built-in case, since built-in types have built-in literals, whereas custom types do not. We'll need a user-defined literal.)
// X, pointA, and pointB defined as in the question.
// User-defined literal X
X operator ""_X (unsigned long long i) { return static_cast<int>(i); }
// Now use an X literal in the sample code instead of an int literal.
int main()
{
pointA pa = 10_X; // OK
//pointB pb = 10_X; // error: conversion from 'X' to non-scalar type 'pointB' requested
}
Here we use only one user-defined conversion, so an implicit conversion is possible as long as the user-defined conversion is not explicit. The constructor of pointB taking an X parameter is marked explicit, so that line fails to compile. In contrast, the pointA line succeeds, just like the case where the literal was a basic type. There is no distinction here, despite the OP's attempt to invent one.
If you don't like user-defined literals (or even if you do), you can get equivalent results by explicitly calling the user-defined conversion to X, as in
pointA pa = X{10}; // Succeeds
//pointB pb = X{10}; // Fails
Again, the effect of marking one constructor explicit is seen.
Leveraging explicit
Time to return the the nominal question being asked. What is the value of the explicit specifier? One benefit is avoiding expensive constructions. If pointA makes a shallow copy of the data in X, while pointB makes a deep copy, there could be good reason to avoid creating pointB objects unless that is explicitly requested. Again, it does not matter whether the parameter is of basic type or of user-defined type.
The question uses variable initialization as one example of this value. I find that a weak example, as changing point p2 = 10; to the more compact point p2{10}; allows the initialization. Those who have adopted the more compact style would not notice a difference due to marking the constructor explicit. There is little value of explicit here. The OP chose a poor example to demonstrate the value of explicit.
One of the better ways to demonstrate the value of explicit utilizes function parameters and function overloading. Consider a function that has been overloaded to accept either a pointA or a pointB parameter.
void fun(const pointA &) { std::cout << "fun(pointA)\n"; }
void fun(const pointB &) { std::cout << "fun(pointB)\n"; }
Let's go with the assumption that pointA is a cheap "view" of an X object, while pointB is an expensive copy. If we have an X object to use as a parameter, we would want to convert it to a pointA for this function. And that is exactly what happens.
int main() {
X test{0}; // Assume we have this object from somewhere.
fun(test); // Converts `test` to `pointA` and calls that overload.
// The overload taking a `pointB` is not called.
}
Both overloads are candidate functions in overload resolution. However, the version taking a pointB parameter is not viable because there is no implicit conversion from X to pointB. Hence, that version is dropped from consideration, leaving only the version taking a pointA parameter.
This demonstrates some of the value of marking constructors explicit. Expensive copies can be avoided, without the programmer having to think about it. In this case, not only is a potential ambiguity avoided, but also it is avoided by automatically choosing the more efficient overload.
Furthermore, this example would remain equally valid if each occurrence of X was replaced by int. The distinction between "basic types" and "custom datatypes" is a figment of the OP's imagination. Don't buy into it.
All C++ operators that I have worked with return something, for example the + operator returns the result of the addition.
Do all C++ operators return something, or are there some C++ operators that do not return anything?
No, not all operators return something.
Although they are probably not exactly what you are thinking about, note that the delete and delete[] C++ 'keywords' are actually operators; and they are defined as having the void return type - which means they evaluate to nothing (which is not 'something').
From cppreference:
void operator delete ( void* ptr ) noexcept;
void operator delete[]( void* ptr ) noexcept;
Operators of custom types can be overloaded to do the most weirdest things.
for example the + operator returns the result of the addition.
Not necessarily:
#include <iostream>
struct foo {
int value = 0;
void operator+(int x) {
value += x;
}
};
int main () {
foo f;
f + 3;
}
Here operator+ adds the left hand side to the value member, and its return type is void. This is a made-up example, but, in general, not returning something from a custom operator is not unusual.
The only operator that can be overloaded and that has the requirement of returning something, that I am aware of, is operator->. It must either return a raw pointer or an object that has an operator->.
To nitpick, operators don't return anything. They are just lexical elements that we use to create expressions in the language. Now, expressions have types and may evaluate to values, and I assume this is what you mean by operators "returning things".
And, well, yes. There are C++ expressions with type void (and consequentially don't evaluate to any value). Some are obvious, others less so. A nice example would be
throw std::runtime_error()
throw is an expression under the C++ grammar. You can use it in other expressions, for instance in the conditional expression
return goodStatus() ? getValue() : throw std::runtime_error();
And the type of a throw expression, is void. Obviously since this just causes execution to rapidly go elsewhere, the expression has no value.
None of the built-in C++ operators return something. Overloaded C++ operators return something insofar that the operator notation is a syntactic sugar for a function call.
Rather, operators all evaluate to something. That something has a well-defined value as well as a type. Even the function call operator void operator()(/*params*/) is a void type.
For example, +'a' is an int type with the value of 'a' encoded on your platform.
If your question is "Can C++ operators have a void return type?" then the answer is most certainly yes.
You can actually define a function call operator to return nothing. For example:
struct Task {
void operator()() const;
};
operator void(): user defined conversion function to void
You may define the peculiar operator void() conversion function, where the compiler will even warn you that the T to void conversion function will never be used:
#include <iostream>
struct Foo {
operator void() { std::cout << "Foo::operator void()!"; }
// warning: conversion function converting 'Foo' to
// 'void' will never be used
};
int main() {
Foo f;
(void)f; // nothing
f.operator void(); // Foo::operator void()!
}
as governed by [class.conv.fct]/1
[...] A conversion function is never used to convert a (possibly
cv-qualified) object to the (possibly cv-qualified) same object
type (or a reference to it), to a (possibly cv-qualified) base class
of that type (or a reference to it), or to (possibly cv-qualified)
void.117
(117)
These conversions are considered as standard conversions for the
purposes of overload resolution ([over.best.ics], [over.ics.ref]) and
therefore initialization ([dcl.init]) and explicit casts. A
conversion to void does not invoke any conversion function
([expr.static.cast]). Even though never directly called to perform a
conversion, such conversion functions can be declared and can
potentially be reached through a call to a virtual conversion function
in a base class.
Whilst, however, as is shown above, you can still invoke it using the explicit .operator void() syntax.
The operators defined (builtin) by the language are tokens used to perform different kinds of computations:
arithmetic (+,-,*,/)
increment/decrement (++,--)
assignment (=,+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=)
logic (!,&&,||)
relational (==,!=,>,<,>=,<=)
conditional ?
comma
and so on. The list can be very extensive.
In language references like this one or this one, these are not necessarily referenced as returning something, just performing an arithmetic or logic operation, a comparison by which means a variable may be modified, etc.
Since this operation results in some value, it may be interpreted as been "returned" by the operator, but it is different from a function return value.
The overloaded operators, on the other hand, can be defined with a return value of some type, even that can be void, so, no, not all operators return some value in C++.
I'm assuming you're talking about operator functions and not operators as a syntactic unit of the language.
If you overload operators on any type, you may actually return whatever you want.
This also makes a lot of sense, because operations like * or () may sometimes very intuitively not return their input type. Imagine multiplying a complex number type with a real number type. Or an operator that returns an element from a collection.
You may also overload the ++ and -- operators to not return anything thus removing the extremely error-prone mixing of side-effect and expression value that the standard version has.
No.
Consider these two examples here:
int multiply (int a, int b) {
return a*b;
}
void multiply_void(int a, int b) {
cout << a*b;
//or cout << multiply(a,b);
}
First function will return an integer value which can be used by another function.
The value is returned and stored in memory to be used when necessary. If not, it is not visible to human & just sits happily in the memory.
Second function will output to the default output device(usually console).
The value returned by the multiplication operator is passed to an output device.
The function multiply_void does not return an actual value.
All operators return something. They are called operators because they operate something , therefore they will return something. Those Operators who do not return something cant be called operators. Either they will return some value or return True or False depending upon the situation.
Operators on their own do not necessarily return anything. Function calls return values. Operators can result in values.
Operators can sometimes invoke functions. Examples include:
• The function call operator.
• An overloaded operator that gets transformed into a function call.
• operator new, which is invoked as part of a new-expression, is a function call.
My only purpose in mentioning function calls is to clarify the result vs. return. Based on looking at all 126 instances of “return” and words derived from it in [expr], the section seems to carefully use the word return to refer to:
• the result of a function call
• aspects of control flow related to coroutines
OK, that’s enough pedantry on result vs. return.
C++ Operators return something or not is depends upon how you used them. Built-In C++ operators return some value until and unless enforced to return void.
Think in a similar fashion like:
1. The bare name of an array is equivalent with the pointer to the first element, without the need to specify index 0.
2. toString() from Java makes it possible to use the name of an object as a string without calling any object method.
Now is there a way in C++ to use the name of a class object to refer to its first member?
Consider:
class Program
{
public:
int id;
char *str;
};
void function(int p)
{
//...
}
and then:
Program prog0;
function(prog0); // instead of function(prog0.id)
Any way to "hide" the member reference?
EDIT:
Why was the holyBlackCat's answer deleted? I was inclining to vote it as the best answer -- no offense, Mateusz. But he was the first to suggest conversion operator and the example was complete and simple.
In C++, such behaviour would be a cataclysm. If I understand correctly, Java tries to convert object of type A to object of type B by searching for first member in A, that is of type B or is implicitly convertible to B.
C++ wasn't designed that way. We like to write code, that is always predictable. You can achieve what you want, but for a price.
The best solution in this case would be conversion operator - consider:
class Program
{
public:
int id;
char *str;
operator int()
{
return this->id;
}
//You can have more than one!
operator const char*()
{
return this->str;
}
};
void function_int(int p)
{
}
void function_str(const char* s)
{
}
Now it is possible to do the following:
Program prog;
function_int(prog); //Equivalent of function_int(prog.id)
function_str(prog); //Equivalent of function_int(prog.str)
The price is, that if you add another int and place it before id it will not be used in conversion, because we stated in our operator explicitly, that "int content" of our class is represented by id and this member is considered when it comes to such conversion.
However, even this simple example shows some potential problems - overloading functions with integral and pointer types could result in very unpredictable behavior. When type contains conversion operators to both pointers and integers, it can get even worse.
Assume, that we have following function:
void func(unsigned long)
{
}
And we call func with argument of type Program. Which conversion operator would you expect to be called? Compiler knows how to convert Program to either int or const char*, but not unsigned long. This article on cppreference should help you to understand how implicit conversions work.
Also, as Barry pointed out, more meaningless constructs become available. Consider this one:
int x = prog + 2
What does it mean? It is perfectly valid code, though. That is why conversion operators should be dosed extremely carefully (in pre-C++11 era, there was a general advise, that every class should have at most one such operator).
Quote from MSDN:
If a conversion is required that causes an ambiguity, an error is generated. Ambiguities arise when more than one user-defined conversion is available or when a user-defined conversion and a built-in conversion exist.
Sometimes, simple solution to this problem is to mark conversion operator with explicit keyword, so you would need to change above calls to:
function_int((int)prog);
function_str((const char*)prog);
It is not as pretty as the previous form, but much safer. It basically means, that compiler is forbidden to perform any implicit conversion using operator marked as explicit. Very useful to avoid ambiguous calls, while still providing some flexibility in code - you can still very easily convert objects of one type to another, but you can be sure when and where these conversions are performed.
However, explicit conversion operators are still not supported by some compilers, as this is C++ 11 feature (for example, Visual C++ 11 doesn't support it).
You can read more about explicit keyword here.
Now is there a way in C++ to use the name of a class object to refer to its first member?
No, C++ doesn't have any reflection, so there's no way to actually determine what the "first member" is.
However, if what you really want is to get an ID for any object, you could just require that object to have that method:
template <typename T>
void function(const T& t) {
int id = t.getID();
// etc.
}
Without knowing more about your use-case, it's hard to know what to propose.
I understand that the keyword explicit can be used to prevent implicit conversion.
For example
Foo {
public:
explicit Foo(int i) {}
}
My question is, under what condition, implicit conversion should be prohibited? Why implicit conversion is harmful?
Use explicit when you would prefer a compiling error.
explicit is only applicable when there is one parameter in your constructor (or many where the first is the only one without a default value).
You would want to use the explicit keyword anytime that the programmer may construct an object by mistake, thinking it may do something it is not actually doing.
Here's an example:
class MyString
{
public:
MyString(int size)
: size(size)
{
}
//... other stuff
int size;
};
With the following code you are allowed to do this:
int age = 29;
//...
//Lots of code
//...
//Pretend at this point the programmer forgot the type of x and thought string
str s = x;
But the caller probably meant to store "3" inside the MyString variable and not 3. It is better to get a compiling error so the user can call itoa or some other conversion function on the x variable first.
The new code that will produce a compiling error for the above code:
class MyString
{
public:
explicit MyString(int size)
: size(size)
{
}
//... other stuff
int size;
};
Compiling errors are always better than bugs because they are immediately visible for you to correct.
It introduces unexpected temporaries:
struct Bar
{
Bar(); // default constructor
Bar( int ); // value constructor with implicit conversion
};
void func( const Bar& );
Bar b;
b = 1; // expands to b.operator=( Bar( 1 ));
func( 10 ); // expands to func( Bar( 10 ));
A real world example:
class VersionNumber
{
public:
VersionNumber(int major, int minor, int patch = 0, char letter = '\0') : mMajor(major), mMinor(minor), mPatch(patch), mLetter(letter) {}
explicit VersionNumber(uint32 encoded_version) { memcpy(&mLetter, &encoded_version, 4); }
uint32 Encode() const { int ret; memcpy(&ret, &mLetter, 4); return ret; }
protected:
char mLetter;
uint8 mPatch;
uint8 mMinor;
uint8 mMajor;
};
VersionNumber v = 10; would almost certainly be an error, so the explicit keyword requires the programmer to type VersionNumber v(10); and - if he or she is using a decent IDE - they will notice through the IntelliSense popup that it wants an encoded_version.
Mostly implicit conversion is a problem when it allows code to compile (and probably do something strange) in a situation where you did something you didn't intend, and would rather the code didn't compile, but instead some conversion allows the code to compile and do something strange.
For example, iostreams have a conversion to void *. If you're bit tired and type in something like: std::cout << std::cout; it will actually compile -- and produce some worthless result -- typically something like an 8 or 16 digit hexadecimal number (8 digits on a 32-bit system, 16 digits on a 64-bit system).
At the same time, I feel obliged to point out that a lot of people seem to have gotten an almost reflexive aversion to implicit conversions of any kind. There are classes for which implicit conversions make sense. A proxy class, for example, allows conversion to one other specific type. Conversion to that type is never unexpected for a proxy, because it's just a proxy -- i.e. it's something you can (and should) think of as completely equivalent to the type for which it's a proxy -- except of course that to do any good, it has to implement some special behavior for some sort of specific situation.
For example, years ago I wrote a bounded<T> class that represents an (integer) type that always remains within a specified range. Other that refusing to be assigned a value outside the specified range, it acts exactly like the underlying intger type. It does that (largely) by providing an implicit conversion to int. Just about anything you do with it, it'll act like an int. Essentially the only exception is when you assign a value to it -- then it'll throw an exception if the value is out of range.
It's not harmful for the experienced. May be harmful for beginner or a fresher debugging other's code.
"Harmful" is a strong statement. "Not something to be used without thought" is a good one. Much of C++ is that way (though some could argue some parts of C++ are harmful...)
Anyway, the worst part of implicit conversion is that not only can it happen when you don't expect it, but unless I'm mistaken, it can chain... as long as an implicit conversion path exists between type Foo and type Bar, the compiler will find it, and convert along that path - which may have many side effects that you didn't expect.
If the only thing it gains you is not having to type a few characters, it's just not worth it. Being explicit means you know what is actually happening and won't get bit.
To expand Brian's answer, consider you have this:
class MyString
{
public:
MyString(int size)
: size(size)
{
}
// ...
};
This actually allows this code to compile:
MyString mystr;
// ...
if (mystr == 5)
// ... do something
The compiler doesn't have an operator== to compare MyString to an int, but it knows how to make a MyString out of an int, so it looks at the if statement like this:
if (mystr == MyString(5))
That's very misleading since it looks like it's comparing the string to a number. In fact this type of comparison is probably never useful, assuming the MyString(int) constructor creates an empty string. If you mark the constructor as explicit, this type of conversion is disabled. So be careful with implicit conversions - be aware of all the types of statements that it will allow.
I use explicit as my default choice for converting (single parameter or equivalent) constructors. I'd rather have the compiler tell me immediately when I'm converting between one class and another and make the decision at that point if the conversion is appropriate or instead change my design or implementation to remove the need for the conversion completely.
Harmful is a slightly strong word for implicit conversions. It's harmful not so much for the initial implementation, but for maintenance of applications. Implicit conversions allow the compiler to silently change types, especially in parameters to yet another function call - for example automatically converting an int into some other object type. If you accidentally pass an int into that parameter the compiler will "helpfully" silently create the temporary for you, leaving you perplexed when things don't work right. Sure we can all say "oh, I'll never make that mistake", but it only takes one time debugging for hours before one starts thinking maybe having the compiler tell you about those conversions is a good idea.
I'm a little confused as to why I've been told to return const foo from a binary operator in c++ instead of just foo.
I've been reading Bruce Eckel's "Thinking in C++", and in the chapter on operator overloading, he says that "by making the return value [of an over-loading binary operator] const, you state that only a const member function can be called for that return value. This is const-correct, because it prevents you from storing potentially valuable information in an object that will be most likely be lost".
However, if I have a plus operator that returns const, and a prefix increment operator, this code is invalid:
class Integer{
int i;
public:
Integer(int ii): i(ii){ }
Integer(Integer&);
const Integer operator+();
Integer operator++();
};
int main(){
Integer a(0);
Integer b(1);
Integer c( ++(a + b));
}
To allow this sort of assignment, wouldn't it make sense to have the + operator return a non-const value? This could be done by adding const_casts, but that gets pretty bulky, doesn't it?
Thanks!
When you say ++x, you're saying "add 1 to x, store the result back into x, and tell me what it was". This is the preincrement operator. But, in ++(a+b), how are you supposed to "store the result back into a+b"?
Certainly you could store the result back into the temporary which is presently holding the result of a+b, which would vanish soon enough. But if you didn't really care where the result was stored, why did you increment it instead of just adding one?
FYI, ++(a + b) is illegal even with PODs (plain old data types, like int). So it makes sense not to allow it for your own class types either. Try this:
int a = 1;
int b = 2;
int c = ++(a+b);
GCC returns error: lvalue required as increment operand.
In your case, it would be preferable make your copy constructor take a const Integer argument, and create your Integer c like this instead:
Integer c(a + b + Integer(1));
Copy constructors usually take a const reference, solving that problem for you.
(Having non-const copy ctor implies some transfer of resources, which can be useful sometimes, but for 99% of all situations, it's not needed)
I believe OP's example would be suitable to the question if the addition operator is substituted with any other binary operator that returns a reference, for example an assignment operator:
Integer c( ++(a = b));
I came here wondering whether I should make my assignment operator return a const or a non-const reference. Some tutorials use non-const versions, contrary to "Thinking in C++"'s advice. And some other references give reasoning behind that:
Notice that the returned reference is not declared const. This can be a bit confusing, because it allows you to write crazy stuff like this:
MyClass a, b, c;
...
(a = b) = c; // What??
At first glance, you might want to prevent situations like this, by having operator= return a const reference. However, statements like this will work with primitive types. And, even worse, some tools actually rely on this behavior. Therefore, it is important to return a non-const reference from your operator=. The rule of thumb is, "If it's good enough for ints, it's good enough for user-defined data-types."