C++ 松耦合隐式转换
当类型在语义上等价时,隐式转换非常有用。例如,假设两个库实现了相同的类型,但名称空间不同。或者只是一个基本相同的类型,除了一些语义上的糖分。现在,您不能将一种类型传递到设计用于使用另一种类型的函数(在其中一个库中),除非该函数是模板。如果不是,您必须以某种方式将一种类型转换为另一种类型。这应该很简单(否则类型就不完全相同了!)但显式调用转换会使代码膨胀,而函数调用大多是无意义的。虽然这样的转换函数实际上可能会复制一些值,但从高级“程序员”的角度来看,它们基本上什么都不做 隐式转换构造函数和运算符显然会有所帮助,但它们引入了耦合,因此其中一种类型必须了解另一种类型。通常,至少在处理库时,情况并非如此,因为其中一种类型的存在使另一种类型变得多余。此外,您不能总是更改库 现在,我看到了如何在用户代码中进行隐式转换的两个选项:C++ 松耦合隐式转换,c++,boost-mpl,enable-if,C++,Boost Mpl,Enable If,当类型在语义上等价时,隐式转换非常有用。例如,假设两个库实现了相同的类型,但名称空间不同。或者只是一个基本相同的类型,除了一些语义上的糖分。现在,您不能将一种类型传递到设计用于使用另一种类型的函数(在其中一个库中),除非该函数是模板。如果不是,您必须以某种方式将一种类型转换为另一种类型。这应该很简单(否则类型就不完全相同了!)但显式调用转换会使代码膨胀,而函数调用大多是无意义的。虽然这样的转换函数实际上可能会复制一些值,但从高级“程序员”的角度来看,它们基本上什么都不做 隐式转换构造函数和运算符
a
添加构造函数:
template <class T> A(
const T& src,
typename boost::enable_if<conversion_enabled<T,A>>::type* ignore=0
)
{
*this = convert(src);
}
模板A(
const T&src,
typename boost::enable_if::type*ignore=0
)
{
*这=转换(src);
}
和一个模板
template <class X, class Y>
struct conversion_enabled : public boost::mpl::false_ {};
模板
已启用结构转换:public boost::mpl::false{};
默认情况下禁用隐式转换
然后,要启用两种类型之间的转换,请专门化模板:
template <> struct conversion_enabled<OtherA, A> : public boost::mpl::true_ {};
template-struct-conversion\已启用:public-boost::mpl::true{};
并实现可通过ADL找到的convert
功能
我个人更喜欢使用第二种变体,除非有强烈的反对理由
现在进入实际问题:为隐式转换关联类型的首选方法是什么?我的建议是好主意吗?这两种方法都有缺点吗?允许这样的转换危险吗?库实现者通常应该提供第二种方法吗?如果他们的类型很可能会在他们最可能使用的软件中复制(我在这里考虑3d渲染中间件,其中大多数包实现3d向量)。我更喜欢你的“代理”方法,而不是其他选项,如果我有麻烦的话
事实是,我发现这是所有开发领域中的一个重大问题,我倾向于避免在与特定库的交互之外使用任何特定于库的构造。一个例子可能是处理各种不同库中的事件/信号。我已经选择boost作为我自己项目代码的一部分,所以我非常有目的地使用boost::signals2进行我自己项目代码中的所有通信。然后,我将接口写入正在使用的UI库
另一个例子是字符串。每个该死的UI库都在重新设计字符串。我所有的模型和数据代码都使用标准版本,并且我为在这种类型下工作的UI包装器提供了接口……只有在我直接与UI组件交互时,才转换为特定于UI的版本
这意味着我不能利用各种独立但相似的构造提供的大量功能,我正在编写大量额外代码来处理这些转换,但这是非常值得的,因为如果我找到更好的库和/或需要切换平台,这样做会变得容易得多,因为我不允许这些东西在所有事情中都以自己的方式进行
所以基本上,我更喜欢代理方法,因为我已经在做了。我在抽象层中工作,这些抽象层将我与我正在使用的任何特定库隔开,并使用与所述库交互所需的细节对这些抽象进行子类化。我一直都在这样做,所以我想知道我想在两个第三方库之间共享信息的一些小地方基本上已经得到了回答。关于您的第一个选项: 提供代理类型,该类型实现 转换运算符和 转换构造函数(和 所有相关人员的任务) 类型,并始终使用它 如果性能不是关键性的(或者可能是关键性的,并且数据基本上都是字符串),则可以使用字符串(文本)作为代理。实现运算符
,您可以使用boost::lexical\u cast
使用文本中间表示进行转换:
const TargetType& foo = lexical_cast<TargetType>(bar);
const TargetType&foo=lexical_cast(条形);
显然,如果您非常关心性能,就不应该这样做,还有其他的注意事项(两种类型都应该有合理的文本表示),但它是相当通用的,并且“只适用于”许多现有的东西。您可以编写一个转换器类(一些代理)可以隐式地从不兼容类型转换为不兼容类型。然后,您可以使用构造函数中的函数从其中一个类型生成代理,并将其传递给该方法。然后将返回的代理直接转换为所需的类型 缺点是必须在所有调用中包装参数。如果操作正确,编译器甚至可以内联完整的调用,而无需实例化代理。类之间没有耦合。只有代理类需要知道它们 我已经有一段时间没做公关了
class Proxy {
private:
IncompatibleType1 *type1;
IncompatibleType2 *type2;
//TODO static conversion methods
public:
Proxy(IncompatibleType1 *type1) {
this.type1=type1;
}
Proxy(IncompatibleType2 *type2) {
this.type2=type2;
}
operator IncompatibleType1 * () {
if(this.type1!=NULL)
return this.type1;
else
return convert(this.type2);
}
operator IncompatibleType2 * () {
if(this.type2!=NULL)
return this.type2;
else
return convert(this.type1);
}
}
expectsType1(Proxy(type2));
expectsType1(Proxy(type1));
expectsType2(Proxy(type1));
class Vector1 {
int x,y,z;
public:
Vector1(int x, int y, int z) : x(x), y(y), z(z) {}
};
class Vector2 {
float x,y,z;
public:
Vector2(float x, float y, float z) : x(x), y(y), z(z) {}
operator Vector1() {
return Vector1(x, y, z);
}
};
void doIt1(const Vector1 &v) {
}
void doIt2(const Vector2 &v) {
}
Vector1 v1(1,2,3);
Vector2 v2(3,4,5);
doIt1(v1);
doIt2(v2);
doIt1(v2); // Implicitely convert Vector2 into Vector1
struct A {
A(float x) : x(x) {}
int x;
};
struct B {
B(int y): y(y) {}
template<class T> B(const T &t) { *this = convert(t); }
int y;
};
inline B convert(const A &a) {
return B(a.x+1);
}