SFINAE 方法在 clang 中完全禁用基类的模板方法

SFINAE method completely disables base class's template method in clang

本文关键字:基类 模板方法 方法 clang SFINAE      更新时间:2023-10-16
#include <iostream>
#include <utility>
struct B {
template<typename T, std::enable_if_t<std::is_same<T, int>::value>* = nullptr>
void foo(T) {
std::cout<<"B::foo"<<std::endl;
}
};
struct D: B {        
using B::foo;
template<typename T, std::enable_if_t<std::is_same<T, std::string>::value>* = nullptr>
void foo(T) {
std::cout<<"D::foo"<<std::endl;
}
};
int main() {
D d;
d.foo(2); // gcc: print "B::foo"; clang: compile error
return 0;
}

假设我们想在派生类 D 中公开foo()重载.gcc 和 Visual Studio 编译并打印"B::foo",正如我预期的那样。但是我收到一个编译错误:

prog.cc:22:7: error: no matching member function for call to 'foo'
d.foo(2);
~~^~~
prog.cc:14:10: note: candidate template ignored: requirement 'std::is_same<int, std::string>::value' was not satisfied [with T = int]
void foo(T) {

这是叮当虫吗?谢谢!

我实际上认为这是一个gcc错误(84832密切相关,尽管正如Wakely指出的那样,这可能是一个核心语言问题(。

从 [namespace.udecl]/15:

using-declarator将基类中的声明引入派生类时,派生类中的成员函数和成员函数模板将覆盖和/或隐藏基类中具有相同名称、参数类型列表、cv 限定符和ref 限定符(如果有(的成员函数和成员函数模板(而不是冲突(。此类隐藏或重写的声明将从using-声明符引入的声明集中排除。

在 [dcl/fct]/5 中定义参数类型列表的地方:

函数的类型使用以下规则确定。每个参数(包括函数参数包(的类型由其自己的decl-specifier-seq声明符确定。确定每个参数的类型后,任何类型为"T 数组"或函数类型 T 的参数都将调整为"指向 T 的指针"。生成参数类型列表后,在形成函数类型时,将删除任何修改参数类型的顶级cv 限定符。转换后的参数类型的结果列表以及省略号或函数参数包的存在与否是函数的参数类型列表

B::fooD::foo具有相同的名称("foo"(,参数类型列表([T](,cv限定符(none(和ref限定符(none(。因此,派生的隐藏了基础的。

如果有人正在寻找解决此问题的方法,您可以在辅助结构中定义额外的重载,然后乘以继承:

#include <iostream>
#include <utility>
struct B {
template<typename T, std::enable_if_t<std::is_same<T, int>::value>* = nullptr>
void foo(T) {
std::cout<<"B::foo"<<std::endl;
}
};
struct C {
template<typename T, std::enable_if_t<std::is_same<T, std::string>::value>* = nullptr>
void foo(T) {
std::cout<<"C::foo"<<std::endl;
}
};
struct D: B, C {
using B::foo;
using C::foo;
};
int main() {
D d;
d.foo(2); // prints B::foo
return 0;
}