特征 class、命名空间和前向声明

traits class, namespace and forward declaration

我目前在使用具有特征 classes 的命名空间时遇到问题。这是我暂定的代码结构:

namespace project {

namespace internal {
template<typename T> struct traits;

} // internal

namespace moduleA {

namespace internal {

class AImpl {

using some_typeA = traits<A>::some_type;
using some_typeAImpl = traits<AImpl>::some_type;
// where to put the traits specialization?? How the forward declaration could be done?

};

} // internal

class A {

A(): imp(new internal::AImpl()) {}
private:
    internal::AImpl* imp;
};

} // moduleA

} // project

以下是我的问题,我正在寻找建议以使此代码更好地遵循既定惯例和最佳实践:

  1. 我正在定义两个内部命名空间,::project::internal::project::moduleA::internal,这是一种不好的做法吗?我对此的担心是,有了两个级别,用户可能更容易从 doxygen 浏览文档,因为所有与 moduleA 相关的东西,包括 moduleA::internal 和不相关的东西,都被组合在一起。
  2. 因为 moduleA::internal::AImpl 依赖于自身 traits<AImpl> 的特征 class,而我的特征模板位于 ::project::internal,所以我必须 (1) 定义一个moduleA::internal 中的特征模板并对其进行专门化; (2) 在::project::internal中定义特征特化。为此,我需要前向声明 AImpl。对于每种情况(1)或(2),究竟应该如何完成?这是否意味着我必须编写这样的代码:
namespace project {
namespace moduleA {class A;}
namespace internal {
template<>
struct traits<module::A> {};
}
namespace moduleA {
... // more code
}
}

看来我对 namespace {} 子句的使用太多了。

  1. 类似于2,module::internal::AImpl依赖于traits<A>,我又需要转发声明A,所以同样的问题。

非常感谢您对此提供帮助,谢谢!

您可以使用函数声明(无需定义),而不是在 C++11 中使用 class 特征模板。可以使用 argument-dependent name lookup 找到函数,这样您就可以在声明 class 的同一名称空间中为 class 特化特征。

这完全消除了必须关闭 class 的命名空间的麻烦,打开 traits 命名空间,使用其完全限定名称为 class 专门化特征,关闭 traits 命名空间,重新打开 class 的命名空间。并且还消除了包含主模板声明的需要。

示例:

#include <type_traits>

template<class T> struct Type {};

template<class T>
void trait_of(Type<T>); // Generic trait version.

namespace N {
struct A;
int trait_of(Type<A>); // Trait specialisation for A.
} // N

int main() {
    using trait_of_a = decltype(trait_of(Type<N::A>{})); // trait_of is found using ADL.
    static_assert(std::is_same<int, trait_of_a>::value, "");
}

trait函数的return类型可以是更多类型的容器,例如:

template<class T>
void more_traits(Type<T>); // Generic trait version. Must be specialized.

namespace N {
struct MoreTraitsOfA {
    using type_X = ...;
    using type_Y = ...;
};
MoreTraitsOfA more_traits(Type<A>); // Trait specialisation for A.
} // N

using MoreTraits = decltype(more_traits(Type<N::A>{})); 
using type_X = MoreTraits::type_X;
using type_Y = MoreTraits::type_Y;