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.
Related
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
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))
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;
};
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
why do we not see a "undefined call to overloaded function" error with the code bellow? just because int is a built in type? where in the standard can I find the guarantee for the conversion to built in type, such as in the code bellow?... thanks!
#include <iostream>
using namespace std;
class B {
public:
operator int(){ return 0; }
};
class A {
public:
A( int i ) { };
};
void f ( int i ) { cout << "overload f(int) was used!";};
void f ( A a ) { cout << "overload f(A) was used!" ;};
int main () {
B b;
f( b );
}
It has nothing to do with being a built-in type. You defined operator int for B. This means that you have provided a user-defined conversion from B to int. According to 12.3.4 of the standard, "at most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value." This is why it is not converted to A, because that would require two implicit conversions.
The rules for determining exactly when this happens are somewhat complicated, so many people advise that you avoid providing user-defined conversions. Another way of defining these is to provide a constructor with one argument; you can add explicit to the beginning to avoid it being applied implicitly.
When you call f(b), the compiler applies the conversion that you provided to convert b to int. If you want to convert it to A, you'll have to define a conversion from B to A, or apply one of the conversions explicitly, like f(int(b)) or f(A(b)).