为什么我不能在 MSVC 中专用化具有返回类型别名模板的模板?

Why can't I specialize a template with an alias template in the return type in MSVC?

提问人:user541686 提问时间:9/11/2023 最后编辑:Jan Schultkeuser541686 更新时间:9/12/2023 访问量:100

问:

MSVC 拒绝编译此内容:

#include <type_traits>

struct G { void operator()() const { } };

template<class T>
using MyTrait = std::is_void<T>;

struct S
{
    template<class F>
    std::enable_if_t<MyTrait<decltype(std::declval<F>()())>::value> run(F &&);
};
template<>
void S::run(G &&);

出现以下错误:

<source>(14): error C2910: 'S::run': cannot be explicitly specialized
<source>(16): error C2760: syntax error: 'int' was unexpected here; expected ';'

但是 Clang 和 GCC 编译得很好。如果我说而不是.std::is_voidMyTrait

为什么会这样?是编译器错误,还是语言中的某些东西导致了这个问题?

C 模板 visual-c++ language-lawyer 编译器-bug

评论

0赞 Swift - Friday Pie 9/11/2023
嗯。。有一个小问题,参数是转发参考。你把它专门作为右值引用,这是有意的吗?无法重现此错误,哪个版本的 MSVC 报告错误? 为我照顾它。run/std:c++17

答:

2赞 Jan Schultke 9/11/2023 #1

这是一个编译器错误。定义成员函数模板的显式专用化是完全合法的。

std::enable_if_t<MyTrait<decltype(std::declval<F>()())>::value>最终是 SFINAE 的别名,使用别名不应阻止专业化。void

在声明器 ID 引用函数模板专用化的声明中,将执行模板参数推导以标识声明所引用的专用化。 具体来说,这是针对显式实例化、显式专用化和某些友元声明完成的。

- [temp.deduc.decl] p1

确定完全专用化是否与成员函数模板匹配的过程与调用 where is 类型的参数的过程相同。void S::run(G&&)S::run(g)gG

在此过程中,哪里会变成 .std::enable_if_t<MyTrait<decltype(std::declval<F>()())>::value>F = Gvoid

值得注意的是,您的示例确实编译了:

template<class F>
std::enable_if_t<MyTrait<decltype(G{}())>::value> run(F&&);

...这几乎是一回事,MSVC 在这里不一致。

有关 MSVC 版本的说明

MSVC 19.35 对此进行了编译,但较新的版本引发了错误。我无法找到此问题的错误报告,可能是因为它相对较新,还没有人注意到/报告它。

错误报告

我已经为此问题提交了错误报告

评论

0赞 303 9/28/2023
非常感谢您向 Microsoft 提交错误报告!我认为还值得注意的是,这个问题的另一种解决方法是从类型特征派生而不是别名,如:template<typename T> struct MyTrait : std::is_void<T> {};