构造函数 SFINAE 和继承在 clang 中失败
Constructor SFINAE and inheritence fails in clang
以下代码在 GCC 下编译正常,但在 clang 中失败并出现错误:
no matching constructor for initialization of 'Bar'
问题似乎是 clang 认为 Foo 的模板构造函数被 Bar 的模板构造函数隐藏或覆盖了。
这是 clang 中的错误还是 GCC 中的非标准化功能?
如何解决这个问题?我无法更改 Foo,因为它是第 3 方。
#include <type_traits>
struct Foo {
Foo() = default;
template<typename T, std::enable_if_t<std::is_trivially_copyable_v<T>>* = nullptr>
Foo(T& object) {}
};
struct Bar : public Foo {
using Foo::Foo;
template<typename T, std::enable_if_t<!std::is_trivially_copyable_v<T>>* = nullptr>
Bar(T& object) {}
};
int main() {
int i;
Bar s{i};
}
添加第二个模板参数和默认参数时,Clang 不会继承构造函数模板。另一方面,在函数的参数列表(C++03 风格)中使用 SFINAE 构造时没有问题:
struct Foo {
Foo() = default;
template<typename T>
Foo(T& object, std::enable_if_t<std::is_trivially_copyable_v<T>>* = nullptr) {}
};
struct Bar : public Foo {
using Foo::Foo;
template<typename T>
Bar(T& object, std::enable_if_t<!std::is_trivially_copyable_v<T>>* = nullptr) {}
};
在此版本中,构造函数模板继承得很好,并且如预期的那样用于重载决议。
将您自己的 SFINAE 检查移动到 c'tor 的参数中,而不更改 Foo
似乎也可以解决它:
struct Foo {
Foo() = default;
template<typename T, std::enable_if_t<std::is_trivially_copyable_v<T>>* = nullptr>
Foo(T& object) {}
};
struct Bar : public Foo {
using Foo::Foo;
template<typename T>
Bar(T& object, std::enable_if_t<!std::is_trivially_copyable_v<T>>* = nullptr) {}
};
Clang 认为原始模板的签名与基础 class 中的签名相同。因此它认为基础 class 版本是隐藏的。
叮当是正确的。 [namespace.udecl]/14:
(emhpasis 我的)
When a using-declarator brings declarations from a base class into a
derived class, member functions and member function templates in the
derived class override and/or hide member functions and member
function templates with the same name, parameter-type-list,
cv-qualification, and ref-qualifier (if any) in a base class (rather
than conflicting). Such hidden or overridden declarations are excluded
from the set of declarations introduced by the using-declarator.
这意味着,对于这种情况,来自 foo
的构造函数模板从 Bar
的构造函数模板的查找中隐藏起来。请注意,只有名称、参数类型列表、cv-qualification 和 ref-qualifier 被认为是隐藏的,即使对于模板也是如此。
以下代码在 GCC 下编译正常,但在 clang 中失败并出现错误:
no matching constructor for initialization of 'Bar'
问题似乎是 clang 认为 Foo 的模板构造函数被 Bar 的模板构造函数隐藏或覆盖了。
这是 clang 中的错误还是 GCC 中的非标准化功能?
如何解决这个问题?我无法更改 Foo,因为它是第 3 方。
#include <type_traits>
struct Foo {
Foo() = default;
template<typename T, std::enable_if_t<std::is_trivially_copyable_v<T>>* = nullptr>
Foo(T& object) {}
};
struct Bar : public Foo {
using Foo::Foo;
template<typename T, std::enable_if_t<!std::is_trivially_copyable_v<T>>* = nullptr>
Bar(T& object) {}
};
int main() {
int i;
Bar s{i};
}
添加第二个模板参数和默认参数时,Clang 不会继承构造函数模板。另一方面,在函数的参数列表(C++03 风格)中使用 SFINAE 构造时没有问题:
struct Foo {
Foo() = default;
template<typename T>
Foo(T& object, std::enable_if_t<std::is_trivially_copyable_v<T>>* = nullptr) {}
};
struct Bar : public Foo {
using Foo::Foo;
template<typename T>
Bar(T& object, std::enable_if_t<!std::is_trivially_copyable_v<T>>* = nullptr) {}
};
在此版本中,构造函数模板继承得很好,并且如预期的那样用于重载决议。
将您自己的 SFINAE 检查移动到 c'tor 的参数中,而不更改 Foo
似乎也可以解决它:
struct Foo {
Foo() = default;
template<typename T, std::enable_if_t<std::is_trivially_copyable_v<T>>* = nullptr>
Foo(T& object) {}
};
struct Bar : public Foo {
using Foo::Foo;
template<typename T>
Bar(T& object, std::enable_if_t<!std::is_trivially_copyable_v<T>>* = nullptr) {}
};
Clang 认为原始模板的签名与基础 class 中的签名相同。因此它认为基础 class 版本是隐藏的。
叮当是正确的。 [namespace.udecl]/14:
(emhpasis 我的)
When a using-declarator brings declarations from a base class into a derived class, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list, cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting). Such hidden or overridden declarations are excluded from the set of declarations introduced by the using-declarator.
这意味着,对于这种情况,来自 foo
的构造函数模板从 Bar
的构造函数模板的查找中隐藏起来。请注意,只有名称、参数类型列表、cv-qualification 和 ref-qualifier 被认为是隐藏的,即使对于模板也是如此。