Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Zero in double vs char* ambiguity

Tags:

c++

I have a handy class in C++ that can be initialized either with an double or with a char*. Everything works as expected except one case when the argument is zero.

struct Var {
    Var (double src) {}
    Var (char *src) {}
};

int main(int argc, char* argv[]) {
    Var A = 123;
    Var B = "abc";
    Var C = 0; <- Error: conversion from 'int' to 'Var' is ambiguous
}

Previously I used int instead of double, and it was fine for some reason.

How can this be fixed?

PS: I know I can use (double)0 or just 0.0, but is there a way to allow just 0 to be accepted as double?

like image 568
exebook Avatar asked Jun 22 '15 14:06

exebook


2 Answers

You could eliminate the ambiguity by adding a third constructor which takes an int and delegates to the double version (requires C++11):

struct Var {
    Var (double src) {}
    Var (const char *src) {}
    //   ^^^^^ should be const
    Var (int src) : Var {static_cast<double>(src)} {}
};

However, this could be solved easier by changing your char* constructor to take a std::string instead and using direct initialization.

like image 153
TartanLlama Avatar answered Oct 30 '22 01:10

TartanLlama


In C++, the literal 0 has type int. You cannot change anything about it being an integer, without adding at least a decimal point. (no actual need to write digits after the point though)

To answer you implicit question regarding why the call was unambiguous with 123 but ambiguous with 0:

  • When you initialized A, it was fine because there is no implicit conversion from an int to a pointer (char*, in this case).

  • When you initialized C using an int the second time, you used 0, the literal 0 can be implicitly converted to a pointer (some legacy behaviour, before C++11 offered nullptr literal).

So, in a nutshell:

int * a = 0; // legal;
int * b = 2; // ILLEGAL;

When initializing C one conversion is needed to give the argument to any of the two available constructors (and both conversions have the same ranking), the call is ambiguous.

To make it unambiguous, you should either cast your int literal to a double ((double)0), use a double literal (0.), or add a constructor taking an int. With this last solution, the constructor taking an int is an exact match, so it is a better match that another overload requiring a conversion (which is why it would be selected).

Edit. Please note that TartanLlama answer should be accepted here as it is a best C++ practice: the constructor taking an C-style string should take a std::string instead. Since there is an implicit conversion from C-style string to std::string, the call will work with little change:

Var B("abc");

But you will isolate your class interface from pointers details.

like image 36
Ad N Avatar answered Oct 30 '22 01:10

Ad N