看看这段代码:
#include <concepts>
using namespace std;
struct A
{
void op();
};
struct B
{
void op( bool );
};
template<typename T>
concept concept_C = is_same<T, A>::value || is_same<T, B>::value;
template<typename T>
requires concept_C<T>
struct C
{
void opA()
requires is_same<T, A>::value;
void opB( bool f )
requires is_same<T, B>::value;
T *pt;
};
template<typename T>
requires concept_C<T>
void C<T>::opA()
requires is_same<T, A>::value
{
pt->op();
}
template<typename T>
requires concept_C<T>
void C<T>::opB( bool f )
requires is_same<T, B>::value
{
pt->op( f );
}
template
struct C<A>;
template
struct C<B>;
MSVC将为A和B实例化opA,也为A和B实例化opB,并给出以下错误:
(42,10): error : too many arguments to function call, expected 0, have 1
(46,8): message : in instantiation of member function 'C<A>::opB' requested here
(7,7): message : 'op' declared here
(34,9): error : too few arguments to function call, expected 1, have 0
(49,8): message : in instantiation of member function 'C<B>::opA' requested here
(12,7): message : 'op' declared here
clang给出了类似的错误,但g++12更聪明,跳过了不适当的编译,即通过它们的概念过滤函数,即,它只编译A和只编译B -所以哪个编译器在这里?有解决办法吗?
[编辑]:大家好,在我编辑这篇文章的同一天,我已经通过安装程序将Visual Studio升级到最新的官方版本。好消息是:这个错误已经修复,代码现在已经正确编译。尽管Visual Studio中的clang已经更新到12。0,它仍然有同样的功能。
看起来GCC是三者中正确的一个。
我[temp。explicit](重点)
11一个显式实例化一个类模板名称专业化也是同样的一个显式实例化(声明或定义)的每一个成员(不包括从基类继承的成员和成员模板)以前没有明确专业翻译单元包含显式实例化提供了相关的约束,如果任何成员被显式实例化的模板参数满足([temp。 construct 。decl] [temp。 construct 。constr]),除了下面描述的以外。
因此,只有在满足相关约束(包括is_same<T A>::value)的情况下,定义才会被实例化。显然情况并非如此,所以MSVC和Clang是错误的。
作为一种解决方法,你可以在if constexpr中重复这个条件。
template<typename T>
requires concept_C<T>
void C<T>::opA()
requires is_same<T, A>::value
{
if constexpr (is_same<T, A>::value)
pt->op();
}
###该代码应该编译。它最近才在gcc中被修复(10。3或11。1及以上版本)。这个问题在gcc的跟踪器中包含了更多的信息:https://gcc。gnu。org/bugzilla/show_bug。cgi?id=96164。
它在clang中被跟踪为https://bugs。llvm。org/show_bug。cgi?id=46684。