我有一种情况,我有一个模板类,它实际上只暴露一个公共函数,并使用一个本地定义的结构用于内部状态,我不想混淆我的标题的公共部分。例如:
template <class T>
class MyClass {
public:
struct InternalState {
// lots of noisy stuff I don't want to see in the public section
};
void userCallableFunction() {
InternalState state;
doPrivateStuff(state);
}
private:
doPrivateStuff(InternalState& state) {
// ok for the internals to go here
}
};
我想将InternalState
的定义推送到私有部分,因为类的客户端不需要查看它。我想减少噪音,所以看起来像这样:
template <class T>
class MyClass {
public:
// only the stuff the client needs to see here
void userCallableFunction() {
InternalState state;
doPrivateStuff(state);
}
private:
// fine for the private noise to be down here
struct InternalState {
};
...
};
问题是:这样做是否合法?即在InternalState
中使用后声明userCallableFunc()
?我知道你不能为非内联/模板化函数执行此操作,但哪些规则适用于内联/模板函数?
编辑:
我在Visual Studio 2010,Clang 4.1和gcc 4.8.1(IdeOne中的示例,以及非模板但inlined case)中尝试了这一点,并且它为所有人成功构建。所以问题是,这样做是否安全和便携?请提供参考,而不是仅仅说'不,你不能这样做'。
答案 0 :(得分:1)
作为unqualified-id(no ::
)并且不具有可以解释为函数的语法,可以应用ADL,语句中的名称InternalState
InternalState state;
使用正常的非限定查找(查找不受限制的名称)查找。
对于成员函数的 body 中的非限定名称,应用特殊规则,请参阅[basic.lookup.unqual] / 8
对于类
X
的成员,成员函数体[...]中使用的名称应以下列方式之一声明:
- 在使用它之前或在封闭块(6.3)或
中使用之前- 应该是类
X
的成员,或者是X
(10.2)的基类成员,或者
请注意,这会对非限定查找施加排序:首先,搜索封闭块,然后,如果未找到任何内容,则搜索类成员。另请注意:由于此上下文中的InternalState
不依赖于模板参数,因此不会搜索基类范围(如果存在基类)。
重要的是有点微妙,不幸的是:应该是班级成员X
不暗示应在成员之前宣布功能体。例如,
struct X
{
int meow()
{
return m; // (A)
}
int m;
} // (B)
;
在行(A)
中,在当前块中找不到名称m
(或任何封闭块,块是复合语句,而不是任何{}
)。因此,它会在X
的整个成员集中查找。只有在X
完成后,即(B)
点
这同样适用于引用嵌套类的名称 - 毕竟,名称查找需要找出名称所指的实体的类型(例如函数,变量,类型)。此外,查找非详细名称IIRC并不区分这些类型。
不幸的是,这是我现在能找到的最好的:/我不高兴,因为它非常微妙,只能间接回答这个问题。
答案 1 :(得分:0)
您只能转发声明InternalState,然后在编译器看到定义之前使用指针或引用:
template <class T>
class MyClass {
public:
// only the stuff the client needs to see here
void userCallableFunction() {
std::unique_ptr<InternalState> state = createInternal();
doPrivateStuff(*state);
}
private:
void doPrivateStuff(InternalState const&);
// fine for the private noise to be down here
struct InternalState {
};
std::unique_ptr<InternalState> createInternal() {
return std::make_unique<InternalState>();
}
};
它类似于PIMPL惯用法,所以你可能想要查看它。