构造函数 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};
}

https://gcc.godbolt.org/z/etvpvF

添加第二个模板参数和默认参数时,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) {}
};

Live example

在此版本中,构造函数模板继承得很好,并且如预期的那样用于重载决议。


将您自己的 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) {}
};

Live example

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 被认为是隐藏的,即使对于模板也是如此。