2

The following code compiles fine under GCC but fails in clang with the error:

no matching constructor for initialization of 'Bar'

The problem seems to be that clang thinks the template constructor of Foo is hidden or overwritten by the template constructor of Bar.

Is this a bug in clang or a non standardized feature in GCC?

How to fix this problem? I cannot change Foo since it is 3rd party.

#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

Viatorus
  • 1,804
  • 1
  • 18
  • 41

2 Answers2

2

Clang is correct. [namespace.udecl]/14:

(emhpasis mine)

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.

That means, for this case, the inheriting constructor template from foo is hidden from lookup by the constructor template from Bar. Note that only name, parameter-type-list, cv-qualification, and ref-qualifier are considered in hiding, even for templates.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • This is for non-constructors. Obviously c'tors don't have cv-qualifiers. C'tors are also... special since they aren't *exactly* brought in. It's a mess really, especially given the template complication. – StoryTeller - Unslander Monica Nov 05 '19 at 07:28
  • @StoryTeller-UnslanderMonica I tried to change them to non-constructor templates, behaviors are the same. It seems [gcc](https://wandbox.org/permlink/2AOX8qSLkOYUm7jj) is wrong; even with non-constructors. – songyuanyao Nov 05 '19 at 07:39
  • I'm not sure. The thing is, the *parameter-type-list* (which is the type after adjustments) can only be known after template argument deduction. Think about it, for something like `foo(T t)`. If I pass an array, the *parameter-type-list* will resolve to a pointer. That can only be known *after* substitution in general. I can't know beforehand if `foo(int*)` really hides `foo(T)`. That becomes apparent only after passing the argument. – StoryTeller - Unslander Monica Nov 05 '19 at 07:41
  • 1
    @StoryTeller-UnslanderMonica I think the point is whether the 2nd template parameter declaration (which is the only different part between two constructors) should be considered in name-hiding, i.e. should it be considered as the part of parameter-type-list, is it right? – songyuanyao Nov 05 '19 at 07:45
  • It's not, of course. The parameter-type-list is defined to contain only the list of function arguments after adjustments. But those are not always known in advance. I'd go further and say that *usually* they aren't known in advance. Clang is sorta judging here in advance that the parameter-type-list will be identical. It's not a judgement it can do in general, so doing it in a few special cases seems wrong. Maybe it's a bug in the standard and not with Clang. I haven't looked at the CWG issue list yet. – StoryTeller - Unslander Monica Nov 05 '19 at 07:49
  • @StoryTeller-UnslanderMonica: As I understand, the adjustment of *parameter-type-list* is to transform `void f(T[])` into `void f(T*)`, so doesn't depend of template argument deduction. – Jarod42 Nov 05 '19 at 11:10
  • 1
    @StoryTeller-UnslanderMonica: *"This is for non-constructors."* Examples also show constructor case: *" with *"`// OK: D2​::​D2(int) hides B1​::​B1(int) and B2​::​B2(int)`"*. – Jarod42 Nov 05 '19 at 11:14
  • 1
    @Jarod42 - Examples are non-normative to begin with. As I said, it's a mess. There is also the matter in namespace.udecl/13, where the template argument list is rightfully considered for the namespace case. And we know Clang knows when to delay the hiding if we use a function parameter instead of a template one. This may be worthy of a CWG issue. There doesn't seem to be one that pertains to it already. – StoryTeller - Unslander Monica Nov 05 '19 at 11:26
  • Anyone attempt to write a CWG issue? Thank you for understanding my problem. – Viatorus Nov 17 '19 at 06:15
0

Clang does to not inherit the constructor template when the second template parameter and default argument is added. On the other hand, it has no problem when the SFINAE construct is used in the function's parameter list (C++03 style):

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

In this version, the constructor template is inherited just fine, and used in overload resolution as one would expect.


Moving your own SFINAE check into the parameter of the c'tor, without changing Foo also seems to resolve it:

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 considers the signature of your original template to be identical to the one from the base class. And it therefore considers the base class version hidden.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • I treat case for non constructor in my answer in [why-should-i-avoid-stdenable-if-in-function-signatures](https://stackoverflow.com/a/49532681/2684539). For me, clang is right (at least for functions), but I would say it is a wording defect. – Jarod42 Nov 05 '19 at 11:21
  • @Jarod42 - I can agree it's a wording defect. To me it seems a template's full signature should be considered. – StoryTeller - Unslander Monica Nov 05 '19 at 11:30