支持两个具有不同语义的整数构造函数

时间:2018-11-23 16:10:44

标签: c++ constructor constructor-overloading

我正在设计一个类,该类具有两个构造函数,这些构造函数的单个整数参数具有不同的语义。

areContentsTheSame
在这种情况下,

template <typename value_t> class Numeric { value_t value_; public: Numeric(value_t value) : value_(value) {} Numeric(long l) { /* computes value_ from l */ } Numeric(double d) { /* computes value_ from d */ } Numeric(std::string bstring) { /* computes value_ from binary string */ } // ... }; 必须是整数类型,它甚至可以是value_t的别名(在这种情况下甚至无法编译)。即使它不是long的类型别名,我也不确定整数提升如何混淆这两个构造函数。

我的想法是我想通过提供基础表示形式()来支持用户构造,方法是提供任何可能的数值表示形式,然后将其转换为基础表示形式(AKA long )。

分隔构造函数的最佳方法是什么?

3 个答案:

答案 0 :(得分:3)

我强烈建议您重新考虑设计。为什么需要两个看起来相同但却做两件事不同的构造函数?

无论如何,您可以使用标签:

struct tag_a{};
struct tag_b{};

template <typename value_t>
class Example {
    value_t value_;
public:
    Example(value_t value, tag_a) : value_(value) {}
    Example(long l, tag_b) {  }
};

用法

long x = 123;
auto a = Example<long>(x,tag_a());
auto b = Example<long>(x,tag_b());

答案 1 :(得分:2)

我建议创建构造函数private,并使用静态函数“创建”实例。静态函数可以具有有意义的名称,以便明确告诉用户对每个函数的期望:

template <typename value_t>
class Example {
    value_t value_;
    //Prevent users to directly create instances
    Example(value_t value): value_(value)
    {
    }
public:
    static Example createFromValue(value_t value)
    {
        return Example(value);
    }
    static Example createComputingValueFromLong(long l)
    {
        return Example(/*Compute from l*/l);
    }
};

即使您仍然想使用这些构造函数而不是分配内部变量,也可以将它们放在私有部分,并通过static函数进行调用,因此用户将永远无法调用它们直。

答案 2 :(得分:1)

  

我正在设计一个类,该类具有两个构造函数,这些构造函数的单个整数参数具有不同的语义。

     

分隔构造函数的最佳方法是什么?


您的代码应反映您的语义。


当某人看到您的类的界面时,他们必须立即猜测其正确用法是什么。由于我不知道用例背后的语义,因此我将自己说明。

处理质量的简单类

想象一下下面的类来处理群众;我们将对其进行增强以提供更多的可构造性:

template<class T>
class Mass
{
    T _kg;
public:
    Mass(T kg) : _kg(kg) {}
    T kg() const { return _kg; }
};

用法:

#include <iostream>
int main()
{
    Mass<double> one_litter_of_water(1.0);
    std::cout << "1 L of water is: " << one_litter_of_water.kg() << " kg\n";
}

Live demo

现在使其能够处理奇怪的单元

现在,我想为用户提供一种简单的方法,用磅(或石头或其他东西)构造Mass

template<class T>
class Mass
{
    T _kg;
public:
    Mass(T kg) : _kg(kg) {}
    Mass(double lb) : _kg(lb/2.2046) {} // ho no!
    T kg() const { return _kg }
};

Live demo

此操作无效,因为T可以是double

使语义显而易见

解决方案就像命名一样简单。对于采用相同类型的多个参数的函数尤其如此。想象有一天,您阅读了一些晦涩的旧代码,然后偶然发现:

draw(2.6, 2.8, 54.1, 26.0); // draw selection

它有什么作用?好吧,显然它吸引了一些东西。它带有四个double参数,它可以是一个矩形。您需要一些时间,去查看draw的声明,找到它的文档,...然后弄清楚它绘制了一个有两点的矩形。可能是一个点,一个宽度和一个高度,可能是很多东西。

在另一种生活中,想象一下,而不是上面发现的那一行:

draw(Point{2.6, 2.8}, Point{54.1, 26.0}); // draw selection

现在不是明显吗?

在大规模案例中使语义显而易见

struct pounds { double value; operator double() const { return value; } };

template<class T>
class Mass
{
    T _kg;
public:
    Mass(T kg) : _kg(kg) {}
    Mass(pounds lb) : _kg(lb/2.2046) {}
    T kg() const { return _kg; }
};

用户可以显然这样使用它:

#include <iostream>
int main()
{
    Mass<double> one_litter_of_water(pounds{2.2046});
    std::cout << "1 L of water is: " << one_litter_of_water.kg() << " kg\n";
}

Live demo

改进

现在,您已经命名了语义,您可以走得更远并提供丰富的重载集:

struct unit { double value; operator double() const { return value; } };
struct pounds : unit {};
struct stones : unit {};
struct grams  : unit {};

template<class T>
class Mass
{
    T _kg;
public:
    Mass(T kg) : _kg(kg) {}
    Mass(pounds lb) : _kg(lb/2.2046) {}
    Mass(stones st) : _kg(st/0.1575) {}
    Mass(grams  g)  : _kg(g/1000.0) {}
    T kg() const { return _kg; }
};

Live demo

请务必注意,逻辑(单位转换)仍在Mass的实现中; poundsstone只是名称:语义。在这种情况下,这可能并不重要(一公斤将长时间保留约0.16颗宝石),但是通常,您应该更喜欢在同一位置封装这些实现细节。