Reputation: 8591
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?
Upvotes: 12
Views: 355
Reputation: 385174
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
}
Upvotes: 0
Reputation: 486
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).
Upvotes: 0
Reputation: 234715
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 private
isation approach is the best method available to you.
Upvotes: 8