Using explicit constructor [duplicate] - c++

This question already has an answer here:
Why does float argument fit to int function parameter?
(1 answer)
Closed 5 years ago.
class foo {
public:
explicit foo(int) { std::cout<<"int constructor"; }
};
int main() {
foo f(0.1);
return 0;
}
I thought explicit keyword was being used to prevent unwanted type conversions, but the above code still works and outputs "int constructor". Why? And how to prevent that in this case?

You're confusing some concepts here.
explicit prevents implicit constructor calls, not constructor calls with implicit parameter type conversions.
Unfortunately the solution is not straightforward. It's not quite newbie-friendly and may require some googling to understand.
You could expicitly delete the constructors for floating-point types: foo(float) = delete; and so on. Then foo f(0.1); will cause 'use of deleted function' error. But you won't be able to do foo f(1l) either, the compiler will complain about 'ambiguous overloads'.
The proper way is following:
class foo
{
public:
foo(int) {std::cout << "int constructor\n";}
template <typename T,
typename = std::enable_if_t<std::is_floating_point_v<T>>>
foo(T) = delete;
};
It's similar to deleting overloads for each floating-point type (as described above), but due to SFINAE, the deleted overload won't be considered for non-floating-point types.

An explicit constructor prevents implicit conversion into your type, from the type taken by the constructor. So in this case, it prevents implicit conversion from int into foo. So:
foo f = 1;
Will not compile. However, this is explicit construction:
foo f(1);
This, of course will work. In your case, you passed a double instead of an int. But there's an implicit conversion from double to int built into the language, that's why it compiles. In other words, your problem is that this compiles:
int x = 0.1;
However, compiling with -Wconversion on gcc (and I believe clang) will raise a warning for this, and if you are compiling with -Werror (which you should), it will turn this into a compilation error. I suspect MSVC has a similar error if you are working with that compiler.

Keyword explicit enforces a compile-time error with message as conversion from ‘int’ to non-scalar type ‘foo’ requested, when you try to do an implicit conversion like this :foo f = 1;. That's all it's expected to do.
Why does it allow float value 0.1 is answered here.
Futhermore if you want to prevent this behavior, use this line of code:
foo(double) = delete;
then you would get an error: use of deleted function ‘foo::foo(double)’ while passing a float/double value.

Related

Avoid or warn on implicit conversion from const char* to bool in GCC

Consider the following code:
void foo(bool parameter) {
std::cout << parameter << "\n";
}
int main() {
foo("const char *argument");
}
I want the compiler to raise a warning when passing const char* instead of bool as a parameter to function foo.
But GCC implicitly converts it. I tried -Wall, -Wextra, and -Wpedantic, but none of these issue a warning. Is there a flag that could catch such an implicit conversion (invalid parameter type)?
Ignore the fact that the function has an argument of type bool, which some may see as bad code style. I can't refactor that part.
The standard just mentions such an implicit conversion will occur:
A prvalue of integral, floating-point, unscoped enumeration, pointer, and pointer-to-member types can be converted to a prvalue of type bool.
I know such behavior is very convenient in if (ptr) statements, but for me, in the case of passing parameters, it is clearly wrong and a source of bugs.
You could declare an overload of foo for pointers as deleted:
template <class T>
void foo(T*) = delete;
Or better yet, as #Ted comments, simply declare a vanilla overload to not compile any implicit conversions:
template <class T>
void foo(T) = delete;
I want the compiler to raise a warning when passing const char* instead of bool as a parameter to function foo. ... I tried -Wall, -Wextra, and -Wpedantic
You need to add -Wconversion to your compiler flags. Note that seems to work with clang (recent or older version), but not with gcc.
If this triggers too many warnings that you don't want to handle, you can selectively enable -Wstring-conversion (clang only).

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.

'explicit' keyword in g++ has no effect for simple constructor (not copy/assignment constructor)? [duplicate]

This question already has answers here:
What does the explicit keyword mean?
(11 answers)
Closed 7 years ago.
Can anyone explain why the following code compiles? I expect it to get an error where the double constant 3.3 can not be converted to int, since I declare the constructor to be explicit.
class A
{
public:
int n;
explicit A(int _n);
};
A::A(int _n)
{
n = _n;
}
int main()
{
A a(3.3); // <== I expect this line to get an error.
return 0;
}
It works in the other way around. Let's define in addition to your code
void f(A a)
{
}
int main()
{
A a(3.3); // <== I expect this line to get an error.
f(5);
return 0;
}
Without the word explicit it would compile, with the explicit it would report an error. The keyword forbids casting from integer to A in situations like this.
explicit class_name ( params ) (1)
explicit operator type ( ) (since C++11) (2)
1) specifies that this constructor is only considered for direct initialization (including explicit conversions)
2) specifies that this user-defined conversion function is only considered for direct initialization (including explicit conversions)
In your case you are using direct initialization to construct an instance of type A by doing this:
A a(3.3);
The explicit keyword does not stop the compiler from implicitly casting your argument from a double type to an int. It stops you from doing something like this:
A a = 33;

How explicit must you be when calling a constructor?

I have this class
struct foo
{
explicit foo(const std::uint32_t& x, const std::uint32_t& y);
};
and a method
int main()
{
std::int32_t x = -1;
std::int32_t y = -1;
foo f(x, y);
}
On my compiler (MSVC2012), this compiles and runs with the values x and y wrapped around to unsigned types. I was not expecting this, but was expecting a compile error due to mismatched types.
What am I missing?
You're out of luck, the standard does allow implicit conversion of signed to unsigned via the creation of an anonymous temporary for an argument passed by constant reference.
(Note this is not true for a non-constant reference).
If you're using C++11, the best thing to do is to delete the constructor using
foo(const std::int32_t& x, const std::int32_t& y) = delete;
Pre C++11 you could make this constructor private and not define it. Rather like the old-fashioned not-copyable idioms.
MSVC2012 is a sort of half-way house C++03 / C++11 compiler. It implements some C++11 features but not others. Unfortunately deletion of constructors is one of the features it does not support so the privateisation approach is the best method available to you.
Actually, you should use the new brace-initialization syntax foo f{x, y} that will at least emit a warning. After that you can configure your compiler to treat warnings as errors and handle them accordingly, as good code should usually get rid of warnings too (because if you wanted the conversion to happen, you should have used an explicit cast).
explicit does not prevent implicit conversion with the constructor arguments (which clearly takes place here when binding the references); it prevents implicit construction.
void bar(foo);
int main()
{
foo f({0, 0}); // doesn't matter that the arguments are implicitly converted
bar({0, 0}); // error - implicit conversion required here to pass a foo
bar(f); // crucially, ok because argument requires no conv. construction
}

Invalid object after elision of copy operation? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why is it an error to use an empty set of brackets to call a constructor with no arguments?
Most vexing parse: why doesn't A a(()); work?
This one gets me mad. Maybe its just too simple.
struct Foo
{
Foo() {}
Foo(const Foo& f) {}
void work() {}
};
int main()
{
Foo f( Foo() );
f.work();
}
GCC 4.6 gives me:
error: request for member ‘work’ in ‘f’, which is of non-class type ‘Foo(Foo (*)())’
After elision of the copy operation the effective code might look like:
int main()
{
Foo f;
f.work();
}
But why can't i call work() ??
Edit:
Yes, duplicate (see below). Didn't find the original post when search first because the source of the symptoms of this is located where i didn't expect that.
Because Foo f( Foo() ); is a function declaration.
I think you want: Foo f;
Or in case you want to copy-construct:
Foo f( (Foo()) );
f is effectively a function declaration within main function.
Try
Foo f((Foo())); // to make the definition of f explicit enough.
n3337 8.2
The ambiguity arising from the similarity between a function-style
cast and a declaration mentioned in 6.8 can also occur in the context
of a declaration. In that context, the choice is between a function
declaration with a redundant set of parentheses around a parameter
name and an object declaration with a function-style cast as the
initializer. Just as for the ambiguities mentioned in 6.8, the
resolution is to consider any construct that could possibly be a
declaration a declaration. [ Note: A declaration can be explicitly
disambiguated by a nonfunction-style cast, by an = to indicate
initialization or by removing the redundant parentheses around the
parameter name. — end note ] [ Example:
struct S {
S(int);
};
void foo(double a) {
S w(int(a));
//function declaration
S x(int());
//function declaration
S y((int)a);
//object declaration
S z = int(a);
//object declaration
}
— end example ]
C++ parser interprets Foo f(Foo()); expression as the function declaration with the signature Foo(Foo(*)()), i.e. a function returning Foo and taking a function pointer to the function returning Foo. Adding explicit parenthesis around the argument like so Foo f((Foo())); will resolve the ambiguity. But consider actually just doing Foo f; which avoids redundant code.