我有一些类定义序列,这些序列的值必须在编译时通过value
成员可用,并在运行时作为类型的实际实例。所以算术序列的基本类型看起来有点像这样,
template<int A, int D>
struct ArithmeticSequence : public Sequence {
ArithmeticSequence(VALUE v)
: Sequence(v) {}
template<unsigned int N>
struct VALUE_N : public VALUE {
static const int value = A+(D*N);
operator int() { return value; }
};
};
class Sequence
目前只定义了一个内部类VALUE
(当前为空)和一个带VALUE
的构造函数,但我将移动operator int()
VALUE_N
进入VALUE
和Sequence
将定义迭代器等,进一步向下。
现在,类应该从ArithmeticSequence
扩展,并为序列的每个成员定义常量。我有两种方法我认为可以用于此,如果我不介意序列的实例能够从相关序列的成员构建(即具有相同初始值和常见差异的序列),我可以使用typedef
:
struct mySequence : public ArithmeticSequence<0,1> {
mySequence(VALUE val = VALUE_N<0>::value)
: ArithmeticSequence(val) {}
typedef VALUE_N<0> zeroth;
typedef VALUE_N<1> first;
// ...
};
如果我这样做,我可以延伸到VALUE_N
:
struct mySequence : public ArithmeticSequence<0,1> {
mySequence(VALUE val = VALUE_N<0>::value)
: ArithmeticSequence(val) {}
struct zeroth : public VALUE_N<0> {};
struct first : public VALUE_N<1> {};
// ...
};
在这两种情况下,我认为我可以使用mySequence::zeroth::value
在编译时获取值,并使用mySequence::zeroth()
来获取运行时对象。但是,使用第二种方法会导致编译器混淆我是在声明函数还是初始化实例,所以我需要mySequence s1 ((mySequence::zeroth()));
而不是mySequence s1 (mySequence::zeroth())
。
现在,我发现以下内容有效,
struct mySequence : public ArithmeticSequence<0,1> {
mySequence(VALUE val = VALUE_N<0>::value)
: ArithmeticSequence(val) {}
struct zeroth : public VALUE_N<0> {};
static const zeroth zeroth;
struct first : public VALUE_N<1> {};
static const first first;
// ...
};
但我的问题(最后)是,关于我随时访问哪一个的规则是什么?我可以使用static const int i = mySequence::zeroth::value
和mySequence s1 (mySequence::zeroth)
,所以正确的事情似乎发生在那里,但如果我说mySequence::zeroth z
而不是将zeroth
视为一个类,它会将其视为变量。在这种情况下这不是问题,因为我不希望人们创建新的mySequence::zeroth
或任何其他值,但我想如果我不明白何时会使用每一个我可能让自己进入在以后遇到麻烦。
对于超长的帖子感到抱歉,并提前感谢你的时间和耐心,让任何人都能做到这一点。我现在想知道我是否应该把所有的背景故事放进去,或者只是简单地问一下这个问题,如果我应该有共识,我会把它编辑下来。感谢。
修改即可。请注意,正如我上面所写,使用struct方法而不是typedef not 提供任何保护,防止使用另一个“相关”序列成员来构造序列对象,这是必要的,我然而,想想最后一个例子是可行的。
答案 0 :(得分:4)
枚举器,函数和对象的名称隐藏了在同一范围内声明的枚举和类的名称。在您的情况下,数据成员名称隐藏结构的名称。您可以通过特殊查找访问隐藏的类型名称:
::
之前的名称。因此,以下详细说明的类型说明符是有效的,并引用类
struct mySequence::zeroth var;
另请注意,在类范围内,成员声明更改了该声明中使用的名称的含义时,它是不正确的。在你的情况下,让我们采取static const first first;
。第一个名称将引用类型,但在mySequence
的完整范围内,该名称将引用数据成员。标准说
在类S中使用的名称N应在其上下文中引用相同的声明,并在完成的S范围内重新评估。违反此规则不需要诊断。
您的编译器不需要诊断它,这是一个短语,意味着它是有效的未定义行为(好的编译器警告您使用“成员更改意义名称”之类的东西)。虽然我怀疑上述规则是否适用于这种情况(因为它的措辞,它当然适用),你可以通过使用精心设计的类型说明符来清除代码
struct first : public VALUE_N<1> { };
static const struct first first;
请注意,您需要在静态成员的类外定义中使用详细类型说明符。有些编译器允许你使用注入的类名来引用类型(GCC在过去做过)
const struct mySequence::first mySequence::first;
以下使用注入的类名。 first
出现在::
之前,并忽略数据成员。但编译器必须查找名称mySequence::first::first
到first
的构造函数而不是其类类型
const mySequence::first::first mySequence::first;
答案 1 :(得分:1)
我必须承认,如果这是你唯一的问题,我不能完全确定,但我认为你只是反对“最令人烦恼的解析”问题。
C ++语法含糊不清。某些C ++块可以通过多种方式进行解析。最令人烦恼的解析的经典形式是:
A a(A());
“明显的”解释是,它是a
类型的对象A
的定义,其初始值为默认构造的A
,但A()
可以被解析为函数声明而不是初始化器。 C ++规则指定如果出现这种歧义,则应首先解释声明,而不是表达式。
这意味着这实际上将a
声明为一个函数(不带参数并返回A
),并返回A
。
消除歧义的一种方法是使用额外的一对括号:
A a( (A()) );
在您的模板中,我认为您只是以稍微伪装的形式遇到过这个问题。