gpt4 book ai didi

c++ - 奇怪的模板编译错误: is this a g++ bug, a Clang bug, or…?

转载 作者:行者123 更新时间:2023-12-01 15:03:43 32 4
gpt4 key购买 nike

我遇到了一些复杂的C++模板代码的编译错误,将其简化如下:


struct MyOptions
{
static const size_t maxArray = 2;
static const uint maxIdx = 8;
};

class OtherClass
{
uint num;
public:
OtherClass(uint val) : num(val)
{
}
void OtherCall(const char *varName, uint arraySize)
{
std::cout << '#' << num << ": " << varName << '[' << arraySize << ']' << std::endl;
}
template <class OPTS_> inline void OtherMethod(const char *varName)
{
OtherCall(varName, OPTS_::maxIdx);
}
};

template <size_t COUNT_> class ConstArray
{
OtherClass *other[COUNT_];
public:
ConstArray(OtherClass *o1, OtherClass *o2) // Just sample logic, shouldn't hard-code 2 elements
{
other[0] = o1;
other[1] = o2;
}
inline OtherClass *operator[](size_t idx) const
{
return other[idx]; // Array itself not changeable by caller
}
};

template <class OPTS_> class MyClass
{
ConstArray<OPTS_::maxArray> others1;
ConstArray<2> others2;
public:
MyClass(OtherClass *o1, OtherClass *o2) : others1(o1, o2), others2(o1, o2)
{ // Just test code to initialize the ConstArray<> members
}
inline void PrintInfo(uint idx, const char *varName)
{
OtherClass *other1Ptr = others1[idx];
other1Ptr->OtherMethod<OPTS_>(varName); // This works
others1[idx]->OtherMethod<OPTS_>(varName); // This FAILS!!
others2[idx]->OtherMethod<OPTS_>(varName); // This works
}
};

int main(int argc, char *argv[])
{
OtherClass a(9), b(42);
MyClass<MyOptions> mine(&a, &b);
mine.PrintInfo(1, "foo");
return 0;
}

g++ 5.4.0中的“此失败!”错误消息上面的线是
error: expected primary-expression before ‘>’ token
others1[idx]->OtherMethod<OPTS_>(varName); // This FAILS!!
^

但是,很明显,当我使用临时 other1Ptr = others1[idx]时,相同的逻辑被很好地分解为2条语句,这使我相信这是一个g++错误。

但是我使用了一个在线编译器在Clang中进行尝试,并得到了不同的(和相互冲突的)错误:
error: use 'template' keyword to treat 'OtherMethod' as a dependent template name
others1[idx]->OtherMethod<OPTS_>(varName); // This fails
^
template
error: use 'template' keyword to treat 'OtherMethod' as a dependent template name
others2[idx]->OtherMethod<OPTS_>(varName); // This works
^
template
2 errors generated.

因此Clang告诉我 others1[idx]->OtherMethods<>()行实际上有什么问题,另外还告诉我在g++中工作的 others2[idx]->OtherMethod<>()行实际上是错误的!

果然,如果我更改PrintInfo()代码,它将在Clang中正常编译:
    inline void PrintInfo(uint idx, const char *varName)
{
OtherClass *other1Ptr = others1[idx];
other1Ptr->OtherMethod<OPTS_>(varName); // This works
// others1[idx]->OtherMethod<OPTS_>(varName); // This FAILS!!
others1[idx]->template OtherMethod<OPTS_>(varName); // This works
// others2[idx]->OtherMethod<OPTS_>(varName); // This works ONLY IN g++!
others2[idx]->template OtherMethod<OPTS_>(varName); // This works
}

而且此代码在g++中也可以正常编译,因此看来这是正确的行为。

但是正如我们已经看到的,g++也接受了
        others2[idx]->OtherMethod<OPTS_>(varName);          // This works ONLY IN g++!

那是g++中的错误吗?还是Clang对于这种逻辑过于严格?解决方法是将 others1[idx]->OtherMethod<>()行分为两部分(带有一个临时变量)实际上是正确的,还是应该以某种方式使用“template”关键字呢?

最佳答案

我认为g++在这里是正确的(尽管clang++具有更好的错误消息措辞),而clang++不正确,无法拒绝others2[idx]->OtherMethod<OPTS_>(varName);语句。尽管正如@walnut在注释中指出的那样,但是clang的最新源代码正确地接受了该语句。

在某些情况下,对template的要求在C++ 17 [temp.names]/4中:

In a qualified-id used as the name in a typename-specifier, elaborated-type-specifier, using-declaration, or class-or-decltype, an optional keyword template appearing at the top level is ignored. In these contexts, a < token is always assumed to introduce a template-argument-list. In all other contexts, when naming a template specialization of a member of an unknown specialization ([temp.dep.type]), the member template name shall be prefixed by the keyword template.



在这里所有相关的情况下,成员模板名称 OtherMethod使用 -> token 显示在类成员访问表达式中。 OtherMethod成员不是“当前实例的成员”,因为在代码的上下文中,只有 MyClass<OPTS_>类型“是当前实例”。因此,通过 [temp.res]/(6.3),仅当对象表达式的类型是依赖的时,名称 OtherMethod才是“未知特化的成员”。

陈述1:
others1[idx]->OtherMethod<OPTS_>(varName);

对象表达式是 *(others1[idx])others1是具有依赖类型 ConstArray<OPTS_::maxArray>的当前实例的成员,因此 others1是类型依赖的( [temp.dep.expr]/(3.1)),而 others1[idx]*(others1[idx])也是类型依赖的( [temp.dep.expr]/1)。 template关键字是必需的。

陈述2:
others2[idx]->OtherMethod<OPTS_>(varName);

这次, others2是当前实例的成员,但是具有非依赖类型 ConstArray<2>。表达式 idx命名了一个非类型的模板参数,因此它是与值相关的( [type.dep.constexpr]/(2.2)),但与类型无关(其类型始终为 uint)。因此 *(others2[idx])与类型无关,并且 template关键字在 OtherMethod之前是可选的。

陈述3:
other1Ptr->OtherMethod<OPTS_>(varName);

对象表达式是 *other1Ptrother1Ptr的类型为 OtherClass*,因此 other1Ptr*other1Ptr都不是类型相关的,关键字 template是可选的。

将语句拆分为两个并不像看起来那样“等效”。在声明中
OtherClass *other1Ptr = others1[idx];

初始化表达式 others1[idx]如上所述是类型相关的,但是您已将特定的非相关类型 OtherClass*赋予了临时变量。如果已将其声明为 auto other1Ptrauto *other1Ptr等,则变量名称将取决于类型。使用显式类型 OtherClass*,这两个语句一起可能更类似于
static_cast<OtherClass*>(others1[idx])->OtherMethod<OPTS_>(varName);

这也是有效的。 (这也不完全等效,因为 static_cast会允许某些转换,而隐式转换则不允许。)

关于c++ - 奇怪的模板编译错误: is this a g++ bug, a Clang bug, or…?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60537846/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com