我正在设计一个类,该类具有两个构造函数,这些构造函数的单个整数参数具有不同的语义。
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
)。
分隔构造函数的最佳方法是什么?
答案 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";
}
现在,我想为用户提供一种简单的方法,用磅(或石头或其他东西)构造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 }
};
此操作无效,因为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";
}
现在,您已经命名了语义,您可以走得更远并提供丰富的重载集:
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; }
};
请务必注意,逻辑(单位转换)仍在Mass
的实现中; pounds
,stone
,等只是名称:语义。在这种情况下,这可能并不重要(一公斤将长时间保留约0.16颗宝石),但是通常,您应该更喜欢在同一位置封装这些实现细节。