4
votes

I have a Parameter class and I've overloaded the constructor to accept bools or doubles. When you give it an int, it fails to build:

error C2668: 'Parameter::Parameter' : ambiguous call to overloaded function could be 'Parameter::Parameter(std::string,std::string,double)' or
'Parameter::Parameter(std::string,std::string,bool)'

I believe I have two options:

  1. Overload with an int default
  2. Explicitly convert my variable to a double

I have a ton of Parameters, and some of them are unsigned long, floats, etc. (in a project multiple people support), so neither of these are perfect solutions. Is there any way to force an implicit conversion from int to double? Thanks.

Code:

#include <string>
#include <unordered_map>
using namespace std;

class Parameter
{
public:
    enum eVarType { BOOL, NUMBER};

    Parameter() {};
    Parameter( string param_name, string param_description, bool dft ) { type_ = BOOL;};
    Parameter( string param_name, string param_description, double dft ) { type_ = NUMBER;};
private:
    eVarType type_;
};

class ParameterManager
{
public:
    template<typename T> void add( string option_name, string description, T value );
private:
    std::unordered_map< string, Parameter > parameters;
};

template<typename T> void ParameterManager::add( string param, string description, T value )
{
    parameters[param] = Parameter( param, description, value );
};

int main()
{
    ParameterManager mgr;
    int var = 1;
    mgr.add("number","This is an int",var); //Could be double or bool: I want it to be a double
}
3

3 Answers

3
votes

If you want to remove the ctor with a bool final element from consideration unless it's an explicit bool, consider templates and SFINAE:

template<class X>
Parameter( string , string , X  ,
    enable_if_t<is_same<decay_t<X>, bool>::value, int> = 0)
{ type_ = BOOL;}

See here in context on coliru.

Or maybe do SFINAE in the template-parameter list:

template<class X,
    enable_if_t<is_same<decay_t<X>, bool>::value, nullptr_t> = nullptr>
Parameter( string , string , X ) { type_ = BOOL;}

Without enable_if_t and decay_t:

template<class X>
Parameter( string , string , X ,
    typename enable_if<is_same<typename decay<X>::type,
    bool>::value, int>::type = 0)
{ type_ = BOOL;}
template<class X,
    typename enable_if<is_same<typename decay<X>::type,
    bool>::value, nullptr_t>::type = nullptr>
Parameter( string , string , X)
{ type_ = BOOL;}
1
votes

You can implement this using traits templates for do the proper cast to double.

#include <string>
#include <unordered_map>
using namespace std;

class Parameter
{
public:
    enum eVarType { BOOL, NUMBER };

    Parameter() {};
    Parameter(string param_name, string param_description, bool dft) { type_ = BOOL; };
    Parameter(string param_name, string param_description, double dft) { type_ = NUMBER; };
private:
    eVarType type_;
};

// Traits definitions.
template<typename T> struct DFTtrait;

template<> struct DFTtrait<double> 
{typedef double Type;};

template<> struct DFTtrait<bool>
{typedef bool Type;};

// Here is the key. When it receives an int
// the type is set to double.
template<> struct DFTtrait<int>
{typedef double Type;};



class ParameterManager
{
public:
    template<typename T, typename cast_t = DFTtrait<T>::Type> void add(string option_name, string description, T value);
private:
    std::unordered_map< string, Parameter > parameters;
};

template<typename T, typename cast_t> void ParameterManager::add(string param, string description, T value)
{
    parameters[param] = Parameter(param, description, (cast_t)value);
};



int main()
{
    ParameterManager mgr;
    int var = 1;
    mgr.add("number", "This is an int", var); // Now DFTtrait<T>::Type is evaluated to double
                                              // hence, (cast_t)value == (double)value when T == int
}

This is perfect for your scenario: I have a ton of Parameters, and some of them are unsigned long, floats, etc... is very easy add more traits (DFTtrait implementations) to match all your needs, for instance, if you add:

template<> struct DFTtrait<long long int>
{typedef double Type;};

your code will automatically support long long int and do the proper cast to double.

-1
votes

A very simple version would be to use a template parameter to the constructor, with a non-template overload for bool (and any other type you don't want to convert to double):

Parameter( string param_name, string param_description, bool dft ) { type_ = BOOL;}
// note, no trailing semicolon required for inline method definitions

template<typename T>
Parameter( string param_name, string param_description, T dft )
{ 
    type_ = NUMBER;
    value = static_cast<double>(dft);
}

(presumably the "real code" actually does something with dft).

If the type is not castable to double then you'll get a compiler error.

The other answers use more advanced versions of this technique to allow you to have T only match certain types (as opposed to my version where it matches every type but then you get an error if invalid types are used).