C++ 可转换类型的设计优于循环依赖

C++ 可转换类型的设计优于循环依赖,c++,dependencies,c++11,circular-dependency,C++,Dependencies,C++11,Circular Dependency,我遇到过这样的代码,它有两个类,可以相互转换,设计是为每个类提供单参数构造函数,每个类使用另一个类,例如,在fileA.hpp class B; class A { explicit A( const B& ); ... }; class A; class B { explicit B( const A& ); ... }; #include "A.hpp" #include "B.hpp" class C { static A toA( const B&

我遇到过这样的代码,它有两个类,可以相互转换,设计是为每个类提供单参数构造函数,每个类使用另一个类,例如,在file
A.hpp

class B;
class A
{
  explicit A( const B& );
  ...
};
class A;
class B
{
  explicit B( const A& );
  ...
};
#include "A.hpp"
#include "B.hpp"
class C
{
  static A toA( const B& );
  static B toB( const A& );
  ...  
};
#include "A.hpp"
#include "B.hpp"
namespace D
{
  A toA( const B& );
  B toB( const A& );
  ...
}
以及在
B.hpp

class B;
class A
{
  explicit A( const B& );
  ...
};
class A;
class B
{
  explicit B( const A& );
  ...
};
#include "A.hpp"
#include "B.hpp"
class C
{
  static A toA( const B& );
  static B toB( const A& );
  ...  
};
#include "A.hpp"
#include "B.hpp"
namespace D
{
  A toA( const B& );
  B toB( const A& );
  ...
}
这在两个类之间引入了循环依赖关系(我宁愿避免,但这可能不是什么大问题)。有更好的设计吗?另一种设计是使用另一个转换器类来完成这项工作,例如在
C.hpp

class B;
class A
{
  explicit A( const B& );
  ...
};
class A;
class B
{
  explicit B( const A& );
  ...
};
#include "A.hpp"
#include "B.hpp"
class C
{
  static A toA( const B& );
  static B toB( const A& );
  ...  
};
#include "A.hpp"
#include "B.hpp"
namespace D
{
  A toA( const B& );
  B toB( const A& );
  ...
}
或命名空间范围的函数,例如在
D.hpp

class B;
class A
{
  explicit A( const B& );
  ...
};
class A;
class B
{
  explicit B( const A& );
  ...
};
#include "A.hpp"
#include "B.hpp"
class C
{
  static A toA( const B& );
  static B toB( const A& );
  ...  
};
#include "A.hpp"
#include "B.hpp"
namespace D
{
  A toA( const B& );
  B toB( const A& );
  ...
}

但我不认为这两个都更好。有没有比这些解决方案更好的替代方案

这里有一种方法可以在两个对象上都使用多态性:

void convert_to_b(const A &a, B &b);
void convert_to_a(const B &b, A &a);

这将只是在A和B之间传输数据,它可以调用两者的成员函数来完成它的工作

这里有一种方法可以在两个对象上都使用多态性:

void convert_to_b(const A &a, B &b);
void convert_to_a(const B &b, A &a);

这将只是在A和B之间传输数据,它可以调用两者的成员函数来完成它的工作

循环依赖本身并不是一件坏事,只要它在您正在建模的真实场景中是有意义的。例如,如果两个类,
BigDecimal
BigInteger
,都表示数字(可能使用不同的表示法),那么让一个类从另一个类构造,并让它双向运行是有意义的

添加第三个类可能是一个更好的选择,但前提是使用它不会迫使您进入“友谊”领域,或迫使您编写实质上次等的代码以避免“友谊”声明。语法也会变得不那么“流畅”。有时,这是一件好事,例如,当您将字符串转换为数字时。但有时额外的<代码>转换为是恼人的,例如,当你从C字符串构造C++字符串时(但不是相反)。
最终,它取决于您的库用户以最全面的方式传达其意图的能力。在类之间引入循环依赖应该是这个目标的次要目标。

循环依赖本身并不是一件坏事,只要它在您正在建模的真实场景中是有意义的。例如,如果两个类,
BigDecimal
BigInteger
,都表示数字(可能使用不同的表示法),那么让一个类从另一个类构造,并让它双向运行是有意义的

添加第三个类可能是一个更好的选择,但前提是使用它不会迫使您进入“友谊”领域,或迫使您编写实质上次等的代码以避免“友谊”声明。语法也会变得不那么“流畅”。有时,这是一件好事,例如,当您将字符串转换为数字时。但有时额外的<代码>转换为是恼人的,例如,当你从C字符串构造C++字符串时(但不是相反)。
最终,它取决于您的库用户以最全面的方式传达其意图的能力。在类之间引入循环依赖关系应该是次要的。我认为可以不使用这两种类型中的一种,而使用另一个类来转换其他类型:

class A {};

class B {
public:
    B(A a) { ... }
    operator A() const { ... }
};

通过这种方式,
B
可以从
a
构造,而
B
可以转换为
a

,我认为可以不使用这两种类型中的一种,而使用另一个类来转换为另一种类型:

class A {};

class B {
public:
    B(A a) { ... }
    operator A() const { ... }
};
通过这种方式,可以从
a
构造
B
,并且可以从注释将
B
转换为
a

一般来说,我会尽量避免使用它们,但我在问题中提到,在这种情况下,它们可能没什么大不了的;然而,如果我有一个完整的类字母表,那么可能一个实用类或名称空间会比a-Z都相互依赖要好,不是吗

如果您有大量可相互转换的类,那么应该在它们之间提供一个公共接口(或者尝试为大型子集提供一个接口)。然后定义一个模板构造函数

class A
{
  template< typename other > // one template instead of many functions
  explicit A( const other &arg, // user just passes by reference as usual
      typename enable_if< // SFINAE restricts template to proper cases
      other::implements_common_interface, // trait signals such cases
      int >::type = 0 ) { // only generate a dummy parameter
      ; // define implementation in header, as with any template
  } // dependency is resolved when template is used, i.e. called

  enum { implements_common_interface = true }; // define trait
};
A类
{
template//一个模板而不是许多函数
显式A(const other&arg,//用户像往常一样通过引用传递
如果::type=0){//只生成一个伪参数
;//在头中定义实现,就像在任何模板中一样
}//使用模板时解析依赖关系,即调用
枚举{implements_common_interface=true};//定义特征
};
现在,共享公共接口并定义
实现\u common\u interface=true的所有类都可以相互转换,而无需任何显式依赖关系。

从注释中

一般来说,我会尽量避免使用它们,但我在问题中提到,在这种情况下,它们可能没什么大不了的;然而,如果我有一个完整的类字母表,那么可能一个实用类或名称空间会比a-Z都相互依赖要好,不是吗

如果您有大量可相互转换的类,那么应该在它们之间提供一个公共接口(或者尝试为大型子集提供一个接口)。然后定义一个模板构造函数

class A
{
  template< typename other > // one template instead of many functions
  explicit A( const other &arg, // user just passes by reference as usual
      typename enable_if< // SFINAE restricts template to proper cases
      other::implements_common_interface, // trait signals such cases
      int >::type = 0 ) { // only generate a dummy parameter
      ; // define implementation in header, as with any template
  } // dependency is resolved when template is used, i.e. called

  enum { implements_common_interface = true }; // define trait
};
A类
{
template//一个模板而不是许多函数
显式A(const other&arg,//用户像往常一样通过引用传递
如果::type=0){//只生成一个伪参数
;//在hea中定义实现