5

I try to overload constructor with int and char *. Then there is ambiguity in call with 0. Is there any workaround/solution for this?

CBigInt (unsigned int);
CBigInt (const char *);

The problem is on the line with 0:

CBigInt a;
// some more code
 a *= 0; 

Thanks for answering.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
ryskajakub
  • 6,351
  • 8
  • 45
  • 75

4 Answers4

10

Make one of the constructors explicit. It will then only be used when passed type exactly matches.

CBigInt (unsigned int);
explicit CBigInt (const char *);
Erik
  • 88,732
  • 13
  • 198
  • 189
  • even better, make both explicit :) – Matthieu M. Mar 31 '11 at 13:36
  • @Matthieu M: Nah, then he'd have to write `a *= CBigInt(0u)`, given the usage scenario OP showed I'd think by default he want integrals to convert to `CBigInt` – Erik Mar 31 '11 at 13:39
  • yes, this works, but I forgot to say, that I want be able to write conversions from chars * as well, like : `a *= "131"` – ryskajakub Mar 31 '11 at 14:04
  • 1
    You could make op *= like this (in addition to the one taking Foo) `template CBigInt & operator*=(T const & V) { return *this *= CBigInt(V); }` - that should work OK together with a few explicit constructors. – Erik Mar 31 '11 at 14:12
6

You could use the "explicit" keyword:

explicit CBigInt(const char *);

Using this, you must explicitly cast the argument to a const char *, otherwise CBigInt(unsigned) will be executed.

mfontanini
  • 21,410
  • 4
  • 65
  • 73
2

The 'explicit' approach works, but may not be intuitive for supporting developers in the future. For cases like these, in previous projects I've worked on, we used static factory methods. We would have a private default constructor and explicitly initialize the members in the static factories. Something like:

class CBigInt {
public:
...
static CBigInt fromUInt(unsigned int i) { 
    CBigInt result; 
    result.value=i; 
    return result; 
}
static CBigInt fromCharPtr(const char* c) { 
    CBigInt result; 
    result.value=parse(c); 
    return result; 
}
...
private:
CBigInt () {}
/*some internal type here*/ value; 
};

This approach removes any ambiguity not only for the compiler, but also for the one who will support your code later.

Assambar
  • 403
  • 1
  • 4
  • 9
1

In this case I would also recommend an explicit constructor, because I think that an arbitrary string (which your constructor takes) does not model a number (which your CBigInt class models). Such cases are what explicit was designed for.

However, this will not work for cases where direct initialization is used

struct A {
  CBigInt n;
  A():n(0) { } // ambiguity again
};

In general, explicit should not be used to resolve internal ambiguities. It should merely be used to forbid the conversion from one type to the other, but not to prefer another constructor over the explicit constructor. In fact, the new C++0x uniform initializations will not ignore explicit constructors in a copy initialization context:

CBigInt f() {
  return { 0 }; // still ambiguous
}

CBigInt b = { 0 }; // still ambiguous

The rules for uniform initialization is: Both constructors are considered, but if an explicit constructor is chosen, initialization is ill-formed.

The literal 0 is an int. Assuming you want to be able to accept all integer types, you need to at least add an int taking constructor. You don't need to add overloads for integer types smaller than int, because those types prefer int over other integer conversions or pointers. Assuming you have an int overload you also need to add overloads for the remaining integer types and, if available and you use it, long long and unsigned long long. Ambiguities will then not arise anymore:

CBigInt (int);
CBigInt (unsigned int);
CBigInt (long);
CBigInt (unsigned long);
// CBigInt (long long);
// CBigInt (unsigned long long);
explicit CBigInt (const char *);
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212