How can we tell the C++ compiler that it should avoid implicit casting while using arithmetic operators such as +and /, i.e.,
size_t st_1, st_2;
int i_1, i_2;
auto st = st_1 + st_2; // should compile
auto i = i_1 + i_2; // should compile
auto error_1 = st_1 + i_2; // should not compile
auto error_2 = i_1 + st_2; // should not compile
// ...
Unfortunately the language specifies what should happen when you add an int to a size_t (see its rules for type promotion) so you can't force a compile time error.
But you could build your own add function to force the arguments to be the same type:
template <class Y>
Y add(const Y& arg1, const Y& arg2)
{
return arg1 + arg2;
}
The constant references prevent any type conversion, and the template forces both arguments to be the same type.
This will always work in your particular case since size_t must be an unsigned type:
The best answer I can give you is to use units: give a look at boost unit.
Another interesting method is the use of opaque typedef you can give a look at this paper Toward Opaque Typedef here a very interesting talk and implementation.
Hope the material can be useful
With built in (non-class) types, it is not possible to prevent unwanted implicit type conversions.
Some compilers can be configured to give warnings for operations involving suspicious conversions, but that does not cover all possible implicit conversions (after all, a conversion from short to long is value preserving, so not all compilers will report it as suspicious). And some of those compiles may also be configured to give errors where they give warnings.
With C++ class types, it is possible to prevent implicit conversions by making constructors explicit, and not defining conversion operators (for example, a class member function named operator int()).
It is also possible for a class type to supply numeric operators (operator+(), etc), which only accept operands of the required types. The problem is that this doesn't necessarily prevent promotion of built in types participating in such expressions. For example, a class that provides an operator+(int) const (so some_object = some_other_object + some_int would work) would not stop an expression like some_other_object + some_short from compiling (as some_short can be implicitly promoted to int).
Which basically means it is possible to prevent implicit conversions to class types, but not prevent promotions occurring in expressions with numeric operators.
Related
I have a variadic template method inside a template class (of type T_) looking like this
template < typename T_ >
class MyContainer {
public:
...
template <typename ...A>
ulong add (A &&...args)
{
T_ t{args...};
// other stuff ...
vec.push_back(t);
// returning an ulong
}
}
So basically I'm trying to make this class adapt to whatever type T_ but since I can't know in advance which types its constructor requires, I use a variadic. I took inspiration from the emplace_back method from the stl library.
Nevertheless, I get a warning of narrowing conversion with integer types if I try to do something like this
MyContainer<SomeClassRequiringAnUlong> mc;
mc.add(2);
warning: narrowing conversion of ‘args#0’ from ‘int’ to ‘long unsigned int’ [-Wnarrowing]
So I was wondering if I can do anything about it. Is there any way to tell the method which parameters' type it is supposed to take according to the template parameter T_ (which is known when the object is created) ?
Is there any way to tell the method which parameters' type it is
supposed to take according to the template parameter T_ (which is
known when the object is created)?
In your case, you should use direct initialization(()) instead of list initialization({}) (to avoid unnecessary narrowing checks).
Consider the case where T is a vector<int>:
MyContainer<std::vector<int>> mc;
mc.add(3, 0);
What do you expect mc.add(3, 0) to do? In your add() function, T_ t{args...} will invoke vector<int>{3,0} and create a vector of size 2, which is obviously wrong. You should use T_ t(args...) to call the overload of vector(size_type count, const T& value) to construct a vector of size 3, just like emplace_back() does.
It is worth noting that due to P0960R3, T_ t(std::forward<Args>(args)...) can also perform aggregate initialization if T_ is aggregate.
In general, no. The rules of C++ explicitly allow implicit conversions to take place. The fact that the authors of C++ made some of those conversions potentially unsafe is another matter.
You could add std::is_constructible<T,A&&...> static_assert or SFINAE to the code to make the compiler errors less ugly if the user inputs wrong arguments, but it won't solve implicit conversions.
From design perspective, the code should not care about this, the purpose of emplace_XXX is to allow exactly the calls that are allowed for T{args...}.
Note: You most likely want to forward the arguments like T element{std::forward<A>(args)...}; and also move the element into the vector vec.push_back(std::move(t));.
That said, the code
T_ t{args...};
//...
vec.push_back(t);
is the exact opposite what emplace functions do, their purpose is to create the element in-place at its final destination. Not to copy or move it there.
You are looking in the wrong direction. It is not the template, but the user code that needs fix. To see the issue, first understand that 2 is of type signed int. So you are trying to construct an object with a signed int, while the constructor expects long unsigned int instead. A minimal repro of the issue can then be written as below.
class SomeClassRequiringAnUlong {
public:
SomeClassRequiringAnUlong(unsigned long) {}
};
int main() {
int v = 2;
SomeClassRequiringAnUlong obj{v};
}
The warning simply states that this narrowing conversion from signed int to long unsigned int is potentially risky and may catch you off guard. E.g.,
int v = -1;
SomeClassRequiringAnUlong obj{v};
still compiles and runs, but the result may not be what you want. Now you see that the issue lies in the user code. I see two ways to fix. 1) Use the type the constructor expects from the very beginning. In your case, that would be changing mc.add(2) to mc.add(2ul). 2ul is of type long unsigned int. 2) Make the type conversion explicit to inform the compiler that the narrowing conversion is by design and it is fine. That would be changing mc.add(2) to mc.add(static_cast<long unsigned int>(2)).
Note that there are issues in your template (though not quite related to the warning) as other answers have noted. But they are irrelevant to the specific question you asked. So I do not further elaborate on them.
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.
I'm working on a legacy library that needs to be backwards compatible with C++03, but is also forward compatible to take advantage of C++11 features like move semantics and explicit casting.
So, is it possible to emulate explicit casting in C++03? I know obviously about the explicit bool (or "safe" bool) idiom - but that's only for casting to a boolean type. Is it possible to emulate a general explicit cast operator in C++03?
I checked around, and found a discussion about this in a book called "Imperfect C++ : Practical Solutions for Real-Life Programming".
In this book, they discuss some ideas about emulating an explicit cast in C++03 (the book was written before C++11). Ultimately, they recommend creating an explicit_cast<T> template. However, I don't like that solution because I want users to simply be able to use static_cast<T>, which works fine in C++11.
So, another solution was to force the compiler to do two conversions, which will disallow implicit conversions. An example of that would be something like:
class int_cast
{
public:
int_cast(const int& v) : m_value(v)
{ }
operator int() const
{
return m_value;
}
private:
int m_value;
};
struct Foo
{
Foo()
{
x = 10;
}
operator int_cast() const
{
return int_cast(x);
}
int x;
};
Here, a Foo should be explicitly convertible to int, but not implicitly. (This code is lifted almost verbatim from Imperfect C++, except in their example they are converting a custom Time object to a std::tm.
However, this doesn't actually work, at least not using GCC 4.7.2:
Foo f;
int x = static_cast<int>(f);
This results in :
test3.cpp: In function ‘int main()’:
test3.cpp:44:28: error: invalid static_cast from type ‘Foo’ to type ‘int’
So I guess "Imperfect C++" is wrong here. The compiler wasn't able to convert a Foo to an int, even with an explicit cast. (Maybe this worked on older compilers?) So, is there anyway to emulate this in C++03 (without using a custom cast operator)?
"Imperfect C++" is right, because it uses a custom "keyword" - actually a function name masquerading as a keyword (not unlike eg.: Tribool's indeterminate). If you try to static_cast you crash against the limitation that the language can only accept conversion chains that involve up to one user-defined type, whereas you have two conversions - from "Foo" to "int_cast" and from there to int.
If you want specifically to be able to static_cast then you'll probably have to hack something with macros to supersede normal static_cast... and accept to live in Undefined Behaviour Land. My preferred choice is to actually work in the inverse direction: simply use explicit_cast and use a macro to redefine it as a static_cast invocation when in C++11 mode. I use explicit cast in my C++ backports toolkit and thus in all the C++ code I write, and I have found no important issues so far.
You can find the text below in Appendix B of the book "C++ Templates The Complete Guide" by David Vandevoorde and Nicolai Josuttis.
B.2 Simplified Overload Resolution
Given this first principle, we are left with specifying how well a
given argument matches the corresponding parameter of a viable
candidate. As a first approximation we can rank the possible matches
as follows (from best to worst):
Perfect match. The parameter has the type of the expression, or it has a type that is a reference to the type of the expression (possibly
with added const and/or volatile qualifiers).
Match with minor adjustments. This includes, for example, the decay of an array variable to a pointer to its first element, or the
addition of const to match an argument of type int** to a parameter of
type int const* const*.
Match with promotion. Promotion is a kind of implicit conversion that includes the conversion of small integral types (such as bool,
char, short, and sometimes enumerations) to int, unsigned int, long or
unsigned long, and the conversion of float to double.
Match with standard conversions only. This includes any sort of standard conversion (such as int to float) but excludes the implicit
call to a conversion operator or a converting constructor.
Match with user-defined conversions. This allows any kind of implicit conversion.
Match with ellipsis. An ellipsis parameter can match almost any type (but non-POD class types result in undefined behavior).
A few pages later the book shows the following example and text (emphasis mine):
class BadString {
public:
BadString(char const*);
...
// character access through subscripting:
char& operator[] (size_t); // (1)
char const& operator[] (size_t) const;
// implicit conversion to null-terminated byte string:
operator char* (); // (2)
operator char const* ();
...
};
int main()
{
BadString str("correkt");
str[5] = 'c'; // possibly an overload resolution ambiguity!
}
At first, nothing seems ambiguous about the expression str[5]. The
subscript operator at (1) seems like a perfect match. However, it is
not quite perfect because the argument 5 has type int, and the
operator expects an unsigned integer type (size_t and std::size_t
usually have type unsigned int or unsigned long, but never type int).
Still, a simple standard integer conversion makes (1) easily viable.
However, there is another viable candidate: the built-in subscript
operator. Indeed, if we apply the implicit conversion operator to str
(which is the implicit member function argument), we obtain a pointer
type, and now the built-in subscript operator applies. This built-in
operator takes an argument of type ptrdiff_t, which on many platforms
is equivalent to int and therefore is a perfect match for the argument
5. So even though the built-in subscript operator is a poor match (by user-defined conversion) for the implied argument, it is a better
match than the operator defined at (1) for the actual subscript! Hence
the potential ambiguity.
Note that the first list is Simplified Overload Resolution.
To remove the "possibly" and "potential" about whether or not int is the same as ptrdiff_t, let's change one line:
str[(ptrdiff_t)5] = 'c'; // definitely an overload resolution ambiguity!
Now the message I get from g++ is:
warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: [enabled by default]
and --pedantic-errors promotes that warning to an error.
So without diving into the standard, this tells you that the simplified list is only part of the story. That list tells you which of several possible routes to prefer when going from A to B, but it does not tell you whether to prefer a trip from A to B over a trip from C to D.
You can see the same phenomenon (and the same g++ message) more obviously here:
struct S {
explicit S(int) {}
operator int() { return 0; }
};
void foo(const S&, long) { }
void foo(int, int) { }
int main() {
S s(0);
foo(s, 1);
}
Again the call to foo is ambiguous, because when we have a choice of which argument to implicitly convert in order to choose an overload, the rules don't say, "pick the more lightweight conversion of the two and convert the argument requiring that conversion".
Now you're going to ask me for the standard citation, but I shall use the fact I need to hit the hay as an excuse for not looking it up ;-)
In summary it is not true here that "a user defined conversion is a better match than a standard integer conversion". However in this case the two possible overloads are defined by the standard to be equally good and hence the call is ambiguous.
str.operator[](size_t(5));
Write code in definite way and you'll not need to bother about all this stuff :)
There is much more to thing about.
Considering the following example, the line int a = objT + 5; gives ambiguous conversion which is handled in two ways, using the explicit cast which i think shouldn't be necessary and replacing the use of conversion operators with member functions instead. and here, my question becomes should you still use conversions operators or not at all?
class Testable {
public:
Testable(int val = 0):x(val){}
operator int() const { return x; }
operator double() const { return x; }
int toInt() { return x; }
double toDouble() { return x; }
private:
int x;
};
int main()
{
Testable objT(10);
// ambiguous conversion
int a = objT + 5;
// why to use explicit cast when
// the conversion operator is for to handle
// this automatically
int b = static_cast<int>(objT) + 5;
// member functions
int c = objT.toInt() + 5;
}
Note that int d = objT; is unambiguous, as is double e = objT; Here the compiler can unambiguously choose a conversion operator because of the exact match between the type of the left hand side and the return types from those conversion operators.
The ambiguous conversion results from objT + 5 (also note: long f = objT; is also ambiguous). The problem is that you've taken away the essential information the compiler needs to mindlessly perform the disambiguation.
The problem goes away if you get rid of either of those conversion operators. Since the underlying data member is an int, I suggest getting rid of operator double().
For your particular case, you could just provide the conversion to double, since it has a standard conversion to int. However, implicit conversions often do more harm than help. If you use them, then avoid having both the conversion from int (your non explicit constructor) and to int, which I think is the source of your current problem.
This question is similar to mine, whether is it possible to change the precedence of implicit conversion operators. Unfortunately there is no satisfactory solution, there is no precedence amongst implicit conversions.
One solution is to have additional overload(s) to the ambiguous function or operator, in your case they are operator + (const Testable&, int) and operator + (const Testable&, double).
Another solution is to simply leave only one implicit conversion, in your case the integer one would be best.
Only use implicit conversion when you desperately need automatic conversion to the given type. Explicit toInt, toDouble functions are MUCH better, they clarify the code and do not hide potential pitfalls. Use the explicit keyword for unary constructors, they block implicit conversion via that constructor.
And no, it is not possible to chain implicit conversions, neither if they are operators nor constructors. The C++ standard mandates only 1 implicit conversion can be in a conversion sequence.
You don't need implicit conversion to double. You always return decimal value and compiler can combine multiple implicit conversion operators.
If your private property x of type int were a double, I would recommend not to use implicit conversion to int since it would cause precision loss.