MSVC refuses to compile this:
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 &&);
with the error:
带有错误:
<source>(14): error C2910: 'S::run': cannot be explicitly specialized
<source>(16): error C2760: syntax error: 'int' was unexpected here; expected ';'
But Clang and GCC compile it just fine. And they all compile it fine if I say std::is_void
instead of MyTrait
.
但是Clang和GCC编译得很好。如果我说std::is_void而不是MyTrait,他们都可以编译它。
Why is this? Is it a compiler bug, or is there something in the language that causes this problem?
为什么会这样?是编译器错误,还是语言中有什么原因导致了这个问题?
更多回答
hmm.. there is slight issue that argument of run
is a forwarding reference. You specialize it as a rvalue reference, is that intended? Cannot reproduce this, which version of MSVC reports an error? /std:c++17
takes care of it for mine.
优秀答案推荐
This is a compiler bug. Defining explicit specializations of member function templates is perfectly legal.
这是一个编译器错误。定义成员函数模板的显式专业化是完全合法的。
std::enable_if_t<MyTrait<decltype(std::declval<F>()())>::value>
is ultimately an alias for void
with SFINAE, and the use of aliases shouldn't prevent specialization.
std::enable_if_t<MyTrait<decltype(std::declval<F>()())>::value>最终是带有SFINAE的void的别名,使用别名不应妨碍专业化。
In a declaration whose declarator-id refers to a specialization of a function template, template argument deduction is performed to identify the specialization to which the declaration refers.
Specifically, this is done for explicit instantiations, explicit specializations, and certain friend declarations.
- [temp.deduc.decl] p1
-[除尘温度下降]p1
The process to determine whether the full specialization void S::run(G&&)
matches the member function template is the same as if you had called S::run(g)
where g
is an argument of type G
.
确定完全专用化的void S::run(G)是否与成员函数模板匹配的过程与调用S::run(G)时的过程相同,其中G是G类型的参数。
During this process, std::enable_if_t<MyTrait<decltype(std::declval<F>()())>::value>
where F = G
would turn into void
.
在此过程中,std::enable_if_t<MyTrait<decltype(std::declval<F>()())>::value>,其中F=G将变为void。
It's worth noting that your example does compile with:
值得注意的是,您的示例编译时使用了:
template<class F>
std::enable_if_t<MyTrait<decltype(G{}())>::value> run(F&&);
... which is pretty much the same thing, and MSVC is being inconsistent here.
……这几乎是一样的事情,MSVC在这里不一致。
Note on MSVC versions
MSVC 19.35 compiles this, but more recent versions raise an error. I was unable to find a bug report for this issue, perhaps because it is relatively recent and no one has noticed/reported it yet.
MSVC 19.35对此进行编译,但最近的版本会引发一个错误。我找不到这个问题的错误报告,可能是因为它相对较新,还没有人注意到/报告它。
Bug report
I've submitted a bug report for this issue.
我已经为这个问题提交了一份错误报告。
更多回答
Much appreciation for submitting the bug report to Microsoft! I think it's also worth noting that another workaround for this problem is to derive from the type-trait instead of aliasing it, as in: template<typename T> struct MyTrait : std::is_void<T> {};
非常感谢您向Microsoft提交错误报告!我认为还值得注意的是,这个问题的另一个解决方法是从类型特征派生,而不是对其进行别名,如:template<typename T>struct MyTrait:std::is_void<T>{};
我是一名优秀的程序员,十分优秀!