6
void func( int radius ) { ... }

which is called like this:

func( 12.4 ); // by user expecting the float to be processed, not realizing there's an implicit conversion

Is there any way to get a warning in such a case from the compiler or runtime environment?

cigien
  • 57,834
  • 11
  • 73
  • 112
azpiuser
  • 311
  • 1
  • 9
  • Just enable all warnings. This is good practice anyway. If you want an actual answer, I suggest you either read your compiler's documentation or tell us what compiler you are using. FWIW, this is a _compile-time_ issue, and the warning would be that the float constant is truncated to an integer. It is not a _runtime-environment_ issue. – paddy Oct 12 '20 at 05:17
  • Is it same as of https://stackoverflow.com/q/57842756/2129218 ? – Build Succeeded Oct 12 '20 at 05:18
  • 3
    Minor point, the comment says `float` but actually `12.4` is a `double`. – cigien Oct 12 '20 at 05:29
  • @cigien: Not entirely a minor point, because I have a codebase which gives warnings for implicit conversion to lower point (what this question is about) when passing 0.0 to a function accepting a float. Exactly how much loss of precision is acceptable? – MSalters Oct 12 '20 at 14:36
  • @MSalters I see what you're saying, but OP only cares about loss of precision from floating point to integral, not about `double` to `float`. Though I think the answer I posted would work there as well (just provide the `double` overload, and `delete` the `float` one). – cigien Oct 12 '20 at 14:38

3 Answers3

7

If you delete the overload of func that accepts a double argument:

void func(double) = delete;

your user will get a hard error:

func(12.4); // error, user can't choose to ignore it

which is even better than a warning.

Here's a demo.

cigien
  • 57,834
  • 11
  • 73
  • 112
2

A deleted primary template with definitions only for specific explicit specializations

If you want to explicitly make sure that there are no unexpected implicit conversions, you could define func as a function template whose primary template definition has been deleted, and where you provide explicit specializations only for the types you want to allow. The deleted primary template will be chosen for types for which there is no exact matching explicit specialization (no implicit conversion in place here).

template<typename T>
void func(T) = delete;

template<>
void func<int>(int) { }

template<>
void func<uint8_t>(uint8_t value) { func<int>(value); }

DEMO.


Deleted and non-deleted overloads depending on type affiliation with a family of types (traits-based using SFINAE)

The approach above, however, would require explicitly typing out an explicit specialization for every single type for which you'd want to allow delegation to some common implementation. This may not be useful unless there is a very small amount of types that should be able to use the overload.

An alternative would be to provide two function template overloads for func, which either has a deleted definition, or a reusable definition, based on a predicate to capture a family of types, e.g. std::is_integral, where each overload leverages SFINAE to be available based on the opposite results of the overload (available for cond or !cond, respectively).

template<typename T,
         typename std::enable_if_t<!std::is_integral_v<T>>* = nullptr>
void func(T) = delete;

template<typename T,
         typename std::enable_if_t<std::is_integral_v<T>>* = nullptr>
void func(T) { }

DEMO

dfrib
  • 70,367
  • 12
  • 127
  • 192
  • This is a nice solution, but it does require explicitly specializing for *all* types where there is no implicit conversion to `int`. Perhaps 2 overloads, a `delete`d one for the unconstrained version, and one for types satisfying `std::integral` would be better. (pre-c++20, this requires sfinae, which is not ideal though). – cigien Oct 12 '20 at 13:18
  • @cigien Good point, I expanded the answer with the SFINAE/delete-or-available approach. – dfrib Oct 12 '20 at 13:43
  • This is much better :) In fact, now this might be a better solution for the OP, since it accepts `long`, but my solution complains about an ambiguous call. Not sure if this matters to the OP, but I think this is more reasonable behavior. – cigien Oct 12 '20 at 13:50
  • 1
    @cigien both are good alternatives, one may work better over another for different particular readers of this thread :) – dfrib Oct 12 '20 at 13:57
0

For your case it is -Wliteral-conversion (clang 10), but if you put that 12.4 into a variable, then it won't be obvious for the compiler that that conversion ist not intended; eg this code

void f(int x) {}

int main() { double x = 3.4; f(x); }

won't give you a warning even with -pedantic flag. If you want to cover this case as well, the best thing you can do is to delete the overridden function as in cigien's answer.

radrow
  • 6,419
  • 4
  • 26
  • 53