Why does float argument fit to int function parameter? - c++

Please, look at this code:
#include <iostream>
class A {
public:
int my;
A(int a=0) : my(a) { }
};
int main() {
A x = 7; // 1
A y = 6.7; // 2
std::cout << x.my << " " << y.my << "\n";
}
It actually compiles although there is no A(double a); constructor.
When exactly compiler is allowed to convert one argument type to another to call corresponding constructor?

cppreference has a list of standard conversions. Of interest to you is the Floating - integral conversions section which can also be found in N4140 4.9/1
A prvalue of floating-point type can be converted to prvalue of any integer type. The fractional part is truncated, that is, the fractional part is discarded.
Finding A(int) to be callable with a standard conversion, the compiler inserts the necessary step to make the code work. It's the same rule that allows int x = 1.1 to compile
If this behavior is undesirable you can forbid it with an =delete
class A {
public:
//...
A(int a);
A(double) =delete;
};

Related

Class variable gets implicitly casted to int despite explicit cast

I wrote implicit cast operator to double and explicit cast operator to int in foo class, and function that takes int as an argument. Despite of being called with not casted foo object code gets executed.
I used gcc PRETTY_FUNCTION and found out that foo object gets casted to double then to int.
class foo
{
public:
int x, y;
foo(int x, int y) : x{x}, y{y} {}
explicit operator int() {cout << __PRETTY_FUNCTION__ << "\n"; return x/y;}
operator double() {cout << __PRETTY_FUNCTION__ << "\n"; return double(x)/double(y);}
};
void printint(int x)
{
cout << x << "\n";
}
int main()
{
foo var(1,2);
printint(var);
}
I expected this not to compile because of explicit operator, but instead it does and prints 0 as int(1/2).
Output of program is:
foo::operator double()
0
From cppreference:
Implicit conversion sequence consists of the following, in this order:
1) zero or one standard conversion sequence;
2) zero or one user-defined conversion;
3) zero or one standard conversion sequence.
Hence your foo can be converted to a double (2) and then the double is converted to an int (3).
If you want the code to not compile you have to also make the double conversion explicit. The explicit only prevents your foo to convert to a int in step 2 alone, but in the whole sequence the conversion can still be done implicitly. Implicit conversion can be tricky and becaue of (1) and (3) a single user provided implicit conversion (2) can have unexpected side effects.
of course it will use the double conversion operator and then cast the double to a int. the double conversion operator can implicitly be called. Remove explicit and it won't cast to double first
It can't use the explicit operator int() because of the explicit

C++ template: no matching function for call

I cannot understand why this program fails to compile both with g++ 7.3 or clang++ 5.0 using -std=c++14.
A can be initialized from a const int as shown. A const reference to A can also be create from a const int but call to f(const A &) with a const int fails. Why?
#include <iostream>
struct V {
int i;
template <class T>
V(const T &t) : i{t} {}
};
struct A {
int i;
A(V v) : i{v.i} {}
};
void f(const A &) {}
int main() {
const auto i = 42;
const A a1{i}; // OK
std::cout << a1.i << '\n'; // 42
const A &a2 = A{i}; // OK
std::cout << a2.i << '\n'; // 42
f(i); // no matching function for call to 'f'
return 0;
}
Given f(i);, copy initialization is applied. And i (with type const int) needs to be converted to A, two user-defined conversions are required; from const int to V, and from V to A. But only one user-defined conversion is allowed in one implicit conversion sequence.
Bot const A a1{i}; and const A &a2 = A{i}; are direct initialization, only one implicit conversion from i (with type const int) to the argument of A's constructor (i.e. V) is required, so they work fine.
Note the difference between copy initialization and direct initialization,
In addition, the implicit conversion in copy-initialization must produce T directly from the initializer, while, e.g. direct-initialization expects an implicit conversion from the initializer to an argument of T's constructor.
As a workaround, you can perform explicit conversion on i before passing it to f().
Converting i to an A for the purpose of the function call will require two user defined conversions (int -> V -> A). The standard places a hard limit of a single user defined conversion on each implicit conversion sequence.
The same would happen if you were to try and bind a2 to i "directly". So you need to do a functional style cast (A{i}) when giving an argument to f as well.
You need two consecutive implicit type conversion here however C++ can do single conversion implicitley for you. If you want to let the compiler to generate the correct typed code for you, use template for the function f like following.
template <typename T>
void f(const T & x) { std::cout << x << std::endl;}
The reason why you need two type conversion is because of having only one constructor which takes type V in the struct. If you want to get rid of two type conversion as a second solution, you can add another constructor which takes int as a parameter like following
struct A {
int i;
A(V v) : i{v.i} {}
A(int theI) : i{theI} { }
};
Two user defined conversions are not supported in copy initialization. Simple work around to the problem is to wrap i with A while passing to funtion f with f(A(i))

Why doesn't converting constructor called in template class?

I write a simple C++ program:
#include <stdint.h>
#include <iostream>
using namespace std;
class B {
public:
B(const bool& value = false) {cout << "B" << endl;}
};
template <typename t1, typename t2>
class A {
public:
A(const t1 &value) {cout << "A1" << endl;};
A(const t2 &value) {cout << "A2" << endl;};
};
int main() {
typedef A<B, int8_t> T;
T v(false);
return 0;
}
Per my understanding, in the following code:
T v(false);
false can trigger B's converting constructor(B(const bool& value = false) {cout << "B" << endl;}) called, so the first A's constructor should run. But in fact, the second A's constructor is called.
So why doesn't converting constructor called?
Calling the first constructor would require a user-defined conversion from bool to B. Calling the second one would require a standard conversion from bool to int8_t. The latter is a better match, which I think is pretty natural.
13.3.3.2 Ranking implicit conversion sequences
2 When comparing the basic forms of implicit conversion sequences (as defined in 13.3.3.1)
— a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and
— a user-defined conversion sequence (13.3.3.1.2) is a better conversion sequence than an ellipsis conversion sequence (13.3.3.1.3).
Keep in mind that bool is considered an integral type in C++, so the conversion to int8_t is indeed a rather unremarkable conversion from one integral type to another. A user-defined conversion to a class is a much more involving process. It is not surprising that language rules favor the simple conversion to int8_t.

Will using a member initializer list make the initialization slightly faster?

In his book "Programming, Principles and practices using C++" Bjarne Stroustrup introduces the concept of member initializer list on pages 314-316 (§ 9.4.4). He uses the following example:
// Simple Date (year, month, day)
class Date
{
public:
Date(int yy, int mm, int dd): y{yy}, m{mm}, d{dd}
{
//...
}
private:
int y, m, d;
};
On page 315 he says:
We could have written:
Date::Date(int yy, int mm, int dd) // constructor
{
y = yy;
m = mm;
d = dd;
}
but then we would in principle first have default initialized the members and then assigned values to them.
Therefore, can I conclude that using member initializer lists makes the code slightly faster? Of course, no one would notice on a modern PC. But I'm planning to use C++ for embedded development.
EDIT:
I'll further specify my question. By "slightly faster" I actually mean "less CPU cycles involved".
I also agree that the potential efficiency increase for this particular example will be near to nothing. But for much larger classes and structs, it might become noticable on a microcontroller.
In the second example you are not initializing, you are assigning to variables which have been already initialized. The variables are initialized (default constructed) before entering the constructor, so you are actually setting them twice.
An int doesn't have any specific default initializer so you don't notice but try with different code as in
#include <iostream>
using namespace std;
class Foo
{
int x;
public:
Foo() : x(0) { cout << "Foo()" << endl; }
Foo(int x) : x(x) { cout << "Foo(int)" << endl; }
Foo& operator=(const Foo& o) {
cout << "Foo::operator=(const Foo&)" << endl;
this->x = o.x; return *this;
}
};
class Bar
{
Foo foo;
public:
Bar(const Foo& foo) { this->foo = foo; }
Bar(bool, const Foo& foo) : foo(foo) { }
};
int main() {
cout << "Assigned in constructor" << endl;
Bar bar = Bar(Foo(5));
cout << "Assigned in initializer list" << endl;
Bar bar2 = Bar(false, Foo(5));
}
This prints
Assigned in constructor
Foo(int)
Foo()
Foo::operator=(const Foo&)
Assigned in initializer list
Foo(int)
so you see they're definitely not equivalent. Indeed, for example, you are not able to assign a const field in a constructor
The C++ standard specifies "default initialization" as follows:
[dcl.init]
To default-initialize an object of type T means:
— if T is
a (possibly cv-qualified) class type (Clause 9), the default
constructor (12.1) for T is called (and the initialization is
ill-formed if T has no default constructor or overload resolution
(13.3) results in an ambiguity or in a function that is deleted or
inaccessible from the context of the initialization);
— if T is an
array type, each element is default-initialized;
— otherwise, no initialization is performed.
Your class members are plain, garden-variety, ints. They are not classes. They are not arrays. Therefore default-initialization, in the case of ints, does nothing.
I will expect most compilers to generate identical code, in both of your examples. It makes no difference, whatsoever.

Why is the 'explicit' keyword allowing implicit conversions?

class Test {
private:
int value;
public:
void display(void)
{
cout << "Value [" << value << "]" << endl;
}
explicit Test(int i)
{
value=i;
}
};
int main() {
Test a(5);
Test b(4.9);
a.display();
b.display();
cin.get();
return 0;
}
Float value gets converted to int even though explicit is mentioned.
I was expecting (incorrectly) that float does not get converted to integer and object b not to be constructed.
explicit refers to the constructor itself, not the constructor's parameters. Your explicit constructor may not be used as an implicit conversion to type Test.
void function( Test param );
function( 5 ); // Your "explicit" makes this call an error.
// The parameter must be explicitly cast, such as Test(5)
In C++11 or later, you can prevent implicit parameter conversions using the = delete syntax on a template parameter.
Test(int i)
{
value=i;
}
template<typename T>
Test(const T&) = delete;
// ^ Aside from your int constructor and the implicitly-generated
// copy and move constructors, this will be a better match for any other type
In C++20 or later, you can prevent implicit parameter conversions using the std::same_as concept.
Test(std::same_as<int> auto i)
{
value=i;
}
explicit just prevents any implicit conversions. So if you had:
void foo(Test t);
You cannot call foo(4); because the Test constructor is explicit. You'd have to call foo(Test(4));. The explicit keyword has nothing to do with any conversions that might have to happen during construction.
From the standard [class.conv.ctor]:
An explicit constructor constructs objects just like non-explicit constructors, but does so only where the
direct-initialization syntax (8.5) or where casts (5.2.9, 5.4) are explicitly used.
Which means that Test t = 4; is also illegal, but Test t(42.0) is fine.
It's a Floating–integral conversion.
That is: it's an implicit conversion between a prvalue of type double to a prvalue of type signed int. It discards the fractional part.
TL;DR: the conversion happens between 'double' and 'int', not in your Test constructor. If you want to prevent that constructor to be called with a float or a double you can add the definition:
Test(double) = delete;
In your Test class. Live on compiler explorer