Clang与GCC对模板构造函数继承的解析差异问题问询
问题:Clang与GCC对继承构造函数的解析分歧
不知为何,Clang拒绝编译以下看似正确的代码,而GCC可以编译通过:
#include <cstdio> #include <utility> #include <concepts> template <typename T> struct Wrapper { T val; }; template <typename T> struct Base { template <typename U> requires std::is_same_v<U, Wrapper<T>> Base(U&&) { puts("Base constructor called"); } }; template <typename T> struct S : Base<T> { using B = Base<T>; using B::B; template <typename U> requires std::is_constructible_v<T, U> S(U&& v) : B( Wrapper<T>{ T(v) } ) { puts("S constructor called"); } }; int main() { // 按预期调用S的构造函数 S<int> a(10); // GCC下按预期调用Base的构造函数,Clang编译失败 S<int> b(Wrapper<int>{1}); }
这段代码使用了C++20 Concepts语法,即使改用SFINAE(包括typename = std::enable_if_t<...>和std::enable_if_t<..., bool> = true两种形式),结果仍然一致:GCC正常编译,Clang报错。
有意思的是,只要做以下任一修改,Clang就能正常编译:
- 移除Base类构造函数的转发引用(
&&) - 移除派生类S构造函数的转发引用(
&&) - 给任一构造函数添加默认参数
这看起来像是Clang认为这两个构造函数的签名相同,从而隐藏了基类的构造函数。根据cppreference关于using声明与继承构造函数的说明:
与其他非静态成员函数的using声明一样,若继承的构造函数与派生类的某个构造函数签名匹配,会被派生类中的版本隐藏,无法被查找。
但如果把基类的构造函数直接移到派生类中,代码却能正常编译——这说明二者的签名其实并不匹配(否则会出现重声明错误)。那么这到底是Clang的Bug,还是我对C++相关规则存在误解?
Clang的报错信息如下:
foobar.cpp:37:12: error: no matching constructor for initialization of 'S<int>' S<int> b(Wrapper<int>{1}); ^ ~~~~~~~~~~~~~~~ foobar.cpp:19:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Wrapper<int>' to 'const S<int>' for 1st argument struct S : Base<T> { ^ foobar.cpp:19:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'Wrapper<int>' to 'S<int>' for 1st argument struct S : Base<T> { ^ foobar.cpp:25:5: note: candidate template ignored: constraints not satisfied [with U = Wrapper<int>] S(U&& v) ^ foobar.cpp:24:36: note: because 'std::is_constructible_v<int, Wrapper<int> >' evaluated to false template <typename U> requires std::is_constructible_v<T, U> ^ 1 error generated.
内容的提问来源于stack exchange,提问作者felps321
相关产品推荐
相关产品推荐

