【发布时间】:2020-03-18 08:26:01
【问题描述】:
我有一些代码无法编译,我将其缩减为以下最低版本:
class Builder
{
public:
Builder()
{}
auto foo(int) -> Builder &
{
return *this;
}
template<typename T>
auto bar() -> Builder &
{
return *this;
}
};
template<typename T>
Builder get_builder()
{
return Builder().foo(T()).bar<T>();
}
int main()
{
auto builder = get_builder<int>();
(void) builder;
}
Clang (9.0.0) 拒绝这样做:
prog.cc:22:31: error: use 'template' keyword to treat 'bar' as a dependent template name
return Builder().foo(T()).bar<T>();
^
template
Clang 是否正确地说 bar 是一个依赖模板名称? VS 2017 没有问题。 GCC (9.2.0) 也拒绝该代码,但带有更模糊的错误消息:
prog.cc: In function 'Builder get_builder()':
prog.cc:22:36: error: expected primary-expression before '>' token
22 | return Builder().foo(T()).bar<T>();
| ^
prog.cc:22:38: error: expected primary-expression before ')' token
22 | return Builder().foo(T()).bar<T>();
|
按照 Clang 的建议更改违规行
return Builder().foo(T()).template bar<T>();
修复了 Clang 和 GCC 的编译。 VS2017 也接受这个版本。
似乎解决方案很简单,但如果我重新排序函数调用:
return Builder().bar<T>().foo(T());
或从foo中删除参数:
return Builder().foo().bar<T>();
错误消失了。
这是怎么回事?
- Clang 和 GCC 是否正确拒绝原始版本?
- Clang 和 GCC 是否正确接受更改的版本(重新排序,更改
foo)? 如果是这样,为什么?有什么区别?
【问题讨论】:
-
我的猜测是,由于您将
T传递给foo,编译器不会尝试找出要调用的foo的哪个重载,而是将其视为从属名称.在这种情况下,只有一个重载,因此从人类的角度来看,这似乎很明显,但很容易想象一个场景有多个foo重载和不同的返回类型。 -
在这种情况下,为什么重新排序函数调用可以解决问题?
-
因为
Builder()不依赖。你使用template bar不是因为bar是依赖的,而是因为foo是依赖的。 -
或者更准确地说是
foo的返回值被认为是依赖的。 -
MSVC允许this你不觉得奇怪吗?
标签: templates gcc clang c++17 language-lawyer