recursively nested templates -- gcc bug? - c++

I have the following snippet.
template< typename T >
struct f
{
template< typename V >
struct a : f
{};
};
int main ()
{
f<int>::a<int>::a<double> x;
}
It compiles with no warnings on GCC 4.4.5 and also MSVC 2010, but not on GCC 4.5.2 -- on which I get the following errors:
test.cc: In function 'int main()':
test.cc:11:21: error: expected primary-expression before 'double'
test.cc:11:21: error: expected ';' before 'double'
So while I don't see anything non-standard about it, the question is obligatory -- is this legal in C++? Also, if it is, how do I file a bug report at GCC? (:
edit: A little background for the curious:
This is supposed to be a piece of template metaprogramming. f basically has the structure of a template metafunction class with apply substituted for a (of course the nested type of apply is omitted so we can concentrate on the structure itself).
Inheritance in this case is a standard device for binding metafunction return values. What this snippet is trying to achieve is a metafunction class that recursively yields itself when evaluated.
edit2: let me put the same snippet a bit differently:
template< typename T >
struct f
{
template< typename V > struct a;
};
template< typename T >
template< typename V >
struct f<T>::a : f<T>
{};
int main ()
{
f<int>::a<int>::a<double> x;
}
This produces the same error. I think it refutes the incomplete type argument.

There were a couple of good notes in existing answers. First, f's type is incomplete at the time the nested class template is defined, but f is a dependent type. Now, if you instantiate the nesting template (f), it will instantiate a declaration of the nested template (the member). Note that the declaration of the member does not include the base clause list, so it doesn't need complete base classes. Once the nesting template has been instantiated implicitly, f is complete and when it comes to instantiating the definition of the member, there should be no issue anymore. So I don't think that comeau is correct complaining here.
The other bug is that, in fact, f<int>::a<int>::a is naming the constructor of a<int>, and requires it to be a constructor template (with <int> being the template arguments). The base of this was DR #147.
The translation to the constructor is not done when the qualifier name isn't the class of the injected class name. For example, if it is a derived class, your code becomes valid (as some answers figured out).

Nice question. This seems to be an issue with gcc for template recursive declaration. Because, had there been solid classes then it gives error and ideally it should be declared as:
struct Out {
struct In;
};
struct Out::In : Out {};

it looks like GCC 4.5 believes you have specified the constructor.
one possible workaround:
template<typename T>
struct f {
template<typename V>
struct a : f {
template<typename Z>
struct q {
typedef a<Z> Q;
};
};
};
int main() {
f<int>::a<int>::q<double>::Q x;
return 0;
}

Using Comeau online, it suggests that f is an incomplete type at the point you use it as a base class, and as such is unsuitable.
"ComeauTest.c", line 5: error: incomplete type is not allowed
struct a : f
I don't have a clang instance at hand unfortunately.

Related

Conversion functions, std::is_base_of and spurious incomplete types: substitution failure IS an error

I'm attempting to implement a conversion function operator, and use std::is_base_of to limit the scope of applicability, but I'm running into issues.
#include <type_traits>
class Spurious;
class MyClassBase {};
template< typename T >
class MyClass: public MyClassBase {
public:
template< typename U, std::enable_if_t< std::is_base_of<MyClassBase, U>::value, bool> = true >
operator U () const {
return U{}; // Complex initialization omitted for brevity
}
};
class MyBool: public MyClass< bool > {};
class MyInt: public MyClass< int > {};
int do_stuff( int i, Spurious const & obj);
int do_stuff( int i, MyBool const & obj) {
return i;
}
int main() {
MyInt const obj;
return do_stuff( 3, obj );
}
The presence of Spurious and the related function definitions means that I'm getting compiler errors (compiler explorer; GCC: error: invalid use of incomplete type 'const class Spurious' Clang:error: incomplete type 'Spurious' used in type trait expression) in the is_base_of implementation.
I get why you can't is_base_of an incomplete type, but I'm not quite understanding why "substitution failure is not an error" isn't applicable here. I would have thought that attempting to is_base_of an incomplete type during template expansion would cause the compiler to just abort the conversion attempt and move on to the next function definition. That's sort of the entire point of the is_base_of statement here: ignore classes which don't match the pattern.
My main question, though, is not the "why", but the "what next". Are there any workarounds? Is there some way to ignore things like Spurious while not requiring them to be fully defined within this compilation unit? Ideally, I'd be able to just modify the definition of the conversion operator (e.g. the SFINAE template parameters) to get it to work.
This isn’t SFINAE, which certainly applies to incomplete types:
template<class T,bool=false>
struct size : std::integral_constant<std::size_t,0> {};
template<class T>
struct size<T,!sizeof(T)> : std::integral_constant<std::size_t,sizeof(T)> {};
static_assert(size<char>::value==1); // OK
struct A;
static_assert(size<A>::value==0); // OK
struct A {};
static_assert(size<A>::value==0); // OK!
The issue here is that std::is_base_of has “undefined behavior” (read: use of it makes a program ill-formed, no diagnostic required) if both types are non-union class types and the latter is incomplete. This isn’t covered by SFINAE both because it’s not in the immediate context and because it’s NDR.
These compilers are choosing to reject the code, which has the benefit of preventing surprises like the // OK! above.
You can use the direct SFINAE approach to avoid this problem:
template< typename T >
class MyClass: public MyClassBase {
public:
template< typename U, std::enable_if_t<
(sizeof(U), std::is_base_of<MyClassBase, U>::value), bool> = true >
operator U () const {
return U{}; // Complex initialization omitted for brevity
}
};
Here there is a direct substitution error in the type of the template parameter if U is incomplete, which prevents instantiating the std::is_base_of specialization that would be IFNDR. Note that using incomplete types with SFINAE this way is still rather dangerous, because the same specialization instantiated in a context where the types in question are complete might produce different behavior (which is also IFNDR, per [temp.point]/7).

Incomplete type is not allowed in a class, but is allowed in a class template

The following is invalid code:
struct foo {
struct bar;
bar x; // error: field x has incomplete type
struct bar{ int value{42}; };
};
int main() { return foo{}.x.value; }
This is quite clear, as foo::bar is considered incomplete at the point where foo::x is defined.
However, there seems to be a "workaround" which makes the same class definition valid:
template <typename = void>
struct foo_impl {
struct bar;
bar x; // no problems here
struct bar{ int value{42}; };
};
using foo = foo_impl<>;
int main() { return foo{}.x.value; }
This works with all major compilers.
I have three questions about this:
Is this indeed valid C++ code, or just a quirk of the compilers?
If it is valid code, is there a paragraph in the C++ standard that deals with this exception?
If it is valid code, why is the first version (without template) considered invalid? If the compiler can figure out the second option, I don't see a reason why it wouldn't be able to figure out the first one.
If I add an explicit specialization for void:
template <typename = void>
struct foo_impl {};
template<>
struct foo_impl<void> {
struct bar;
bar x; // error: field has incomplete type
struct bar{ int value{42}; };
};
using foo = foo_impl<>;
int main() { return foo{}.x.value; }
It once again fails to compile.
The real answer might be ¯\_(ツ)_/¯, but it's probably currently okay because templates are magical, but it may be more explicitly not okay pending some other core issue resolutions.
First, the main problem of course is [class.mem]/14:
Non-static data members shall not have incomplete types.
This is why your non-template example is ill-formed. However, according to [temp.point]/4:
For a class template specialization, a class member template specialization, or a specialization for a class member of a class template, if the specialization is implicitly instantiated because it is referenced from within another template specialization, if the context from which the specialization is referenced depends on a template parameter, and if the specialization is not instantiated previous to the instantiation of the enclosing template, the point of instantiation is immediately before the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.
Which suggests that foo_impl<void>::bar is instantiated before foo_impl<void>, and hence it's complete at the point where the non-static data member of type bar is instantiated. So maybe it's okay.
However, core language issues 1626 and 2335 deal with not-exactly-the-same-but-still-quite-similar issues regarding completeness and templates, and both point to desiring to make the template case more consistent with the non-template case.
What does all of this mean when viewed as a whole? I'm not sure.
I think this example is explicitly allowed by
17.6.1.2 Member classes of class templates [temp.mem.class]
1
A member class of a class template may be defined outside the class template definition in which it is declared.
[Note: The member class must be defined before its first use that requires an instantiation (17.8.1) For example,
template<class T> struct A {
class B;
};
A<int>::B* b1; // OK: requires A to be defined but not A::B
template<class T> class A<T>::B { };
A<int>::B b2; // OK: requires A::B to be defined
—end note ]
This should work fine too:
template <typename = void>
struct foo_impl {
struct bar;
bar x; // no problems here
};
template<typename T>
struct foo_impl<T>::bar{ int value{42}; };
using foo = foo_impl<>;
int main()
{
return foo{}.x.value;
}
More details about the accepted answer
I am not sure that the accepted answer is the correct explanation, but it is the most plausible one for now. Extrapolating from that answer, here are the aswers to my original questions:
Is this indeed valid C++ code, or just a quirk of the compilers? [ It is valid code. ]
If it is valid code, is there a paragraph in the C++ standard that deals with this exception? [ [temp.point]/4 ]
If it is valid code, why is the first version (without template) considered invalid? If the compiler can figure out the second option, I don't see a reason why it wouldn't be able to figure out the first one. [ Because C++ is weird - it handles class templates differently than classes (you could have probably guessed this one). ]
Some more explanations
What seems to be happening
When instantiating foo{} in main the compiler instantiates an (implicit) specialization for foo_impl<void>. This specialization references foo_impl<void>::bar on line 4 (bar x;). The context is within a template definition so it depends on a template parameter, and the specialization foo_impl<void>::bar is obviously not previously instantiated, so all the preconditions for [temp.point]/4 are fulfilled, and the compiler generates the following intermediate (pseudo)code:
template <typename = void>
struct foo_impl {
struct bar;
bar x; // no problems here
struct bar{ int value{42}; };
};
using foo = foo_impl<>;
// implicit specialization of foo_impl<void>::bar, [temp.point]/4
$ struct foo_impl<void>::bar {
$ int value{42};
$ };
// implicit specialization of foo_impl<void>
$ struct foo_impl<void> {
$ struct bar;
$ bar x; // bar is not incomplete here
$ };
int main() { return foo{}.x.value; }
About specialization
As per [temp.spec]/4:
A specialization is a class, function, or class member that is either instantiated or explicitly specialized.
so the call to foo{}.x.value in the original implementation with templates qualifies as a specialization (this was something new to me).
About the version with explicit specialization
The version with explicit specialization does not compile as it seems that:
if the context from which the specialization is referenced depends on a template parameter
no longer holds, so the rule from [temp.point]/4 does not apply.
I'll answer the third part of your question - as IANALL (not a language lawyer).
The code is invalid for the same reason it's invalid to use a function before it has been declared - even though the compiler can figure out what the function's supposed to be by going further down in the same translation unit. And the cases are similar also in the sense that if you happen to have just a declaration with no definition, that's good enough for the compiler, while here you happen to have a template definition before the instantiation.
So the point is: The language standard mandates that the compiler does not look ahead for you when you want to define something (and a class template is not a definition of a class).

Private member access in template substitution and SFINAE

class A { int a; };
template<typename, typename = void>
class test {};
template<typename T>
class test<T,decltype(T::a)> {};
int main() { test<A> a; }
The code above compiles without error on clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final), but fails to compile on g++-5 (Ubuntu 5.4.1-2ubuntu1~16.04) 5.4.1 20160904 and g++-6 (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901 with errors like this:
main.cpp: In function ‘int main()’:
main.cpp:9:22: error: ‘int A::a’ is private within this context
int main() { test<A> a; }
^
main.cpp:1:15: note: declared private here
class A { int a; };
In both cases I compiled with -std=c++11, but the effect is the same for -std=c++14 and -std=c++1z.
Which compiler is correct here? I assumed that, at least since C++11, accessing private members during template substitution should trigger SFINAE, implying that clang is correct and gcc is not. Is there some additional rule I am unaware of?
For referencce, I am thinking of the note in §14.8.2/8 of the current standard draft N4606:
If a substitution results in an invalid type or expression, type
deduction fails. An invalid type or expression is one that would be
ill-formed, with a diagnostic required, if written using the
substituted arguments. [ Note: If no diagnostic is required, the
program is still ill-formed. Access checking is done as part of the
substitution process. — end note ]
Using a member function and a member function pointer instead is accepted by both compilers:
class A { void a() {}; };
template<typename, typename = void>
class test {};
template<typename T>
class test<T,decltype(&T::a)> {};
int main() { test<A> a; }
This is very interesting! I think it's a g++ compiler bug and I think that this is what happens. I've tried several alterations of your code with g++ 4.9.3 and clang 3.7.0 .
Although there are somewhat different rules to function vs class template instantiation, I believe that these are the general steps to a template instantiation:
Compiler reads source file with template definitions.
Name lookup (may trigger ADL) : It is the procedure by which a name,
when encountered in a program, is associated with the declaration
that introduced it.
(http://en.cppreference.com/w/cpp/language/lookup)
Template argument specification / deduction : In order to
instantiate a function template, every template argument must be
known, but not every template argument has to be specified. When
possible, the compiler will deduce the missing template arguments
from the function arguments.
(http://en.cppreference.com/w/cpp/language/template_argument_deduction)
Template substitution (may trigger SFINAE) : Every uses of a
template parameter in the function parameter list is replaced with
the corresponding template arguments.Substitution failure (that is,
failure to replace template parameters with the deduced or provided
template arguments) of a function template removes the function
template from the overload set. (http://en.cppreference.com/w/cpp/language/function_template#Template_argument_substitution)
Forming the overload set: Before overload resolution begins, the
functions selected by name lookup and template argument deduction
are combined to form the set of candidate functions.
(http://en.cppreference.com/w/cpp/language/overload_resolution#Details)
Overload resolution : In general, the candidate function whose
parameters match the arguments most closely is the one that is
called.
(http://en.cppreference.com/w/cpp/language/overload_resolution)
Template Instantiation : the template arguments must be determined
so that the compiler can generate an actual function (or class, from
a class template). (http://en.cppreference.com/w/cpp/language/function_template#Function_template_instantiation)
Compiler generates code.
I'll keep these bullet-points as guidelines and references for later on. Furthermore, I'll refer to template evaluation from steps 1-6. If you find anything wrong in the above list please feel free to change it or comment so that I can make the changes.
In the following example:
class A {};
template<typename, typename = void>
struct test
{ test(){std::cout<< "Using None" <<std::endl;} };
template<typename T>
struct test<T, decltype(T::a)>
{ test(){std::cout<< "Using T::a" <<std::endl;} };
int main()
{ test<A> a; }
Output from both compilers:
Using None
This example compiles fine in both g++ and clang, because, when the compiler completes the evaluation process of all the templates, it will only choose to instantiate the first template for being the best match to the template arguments used to create the object in main(). Also, the template substitution process fails when the compiler fails to deduce T::a (SFINAE). Furthermore, due to the argument mismatch, the specialization will not be included in the overload set and will not be instantiated.
Should we add the second template argument, like this:
test<A , decltype(A::a)> a;
The code will not compile and both compilers would complain of:
error: no member named 'a' in 'A'
In the following example however, things start becoming weird:
class A { int a; };
template<typename, typename = void>
struct test
{ test(){std::cout<< "Using None" <<std::endl;} };
template<typename T>
struct test<T, decltype(T::a)>
{ test(){std::cout<< "Using T::a" <<std::endl;} };
int main()
{ test<A> a; }
Output from clang:
Using None
Output from g++:
error: ‘int A::a’ is private
To begin with, I think that this would have been a nice warning. But why an error? The template won't even get instantiated. Considering the previous example, and the fact that pointers-to-members are constant values known at compile time, it seems, that when clang completes the template evaluation stage, with the SFINAE occuring at template substitution, it accurately instantiates the first template and ignores the specialization. But when g++ goes through the substitution process, and looks for the variable T::a, it sees that it is a private member, and instead of saying SFINAE, it prompts with the error above. I think that this is where the bug is, considering this bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61806
Now, the curious part is in the next example, which uses the private member function:
class A{ void a() {}; };
template<typename, typename = void>
struct test
{ test(){std::cout<< "Using None" <<std::endl;} };
template<typename T>
struct test<T,decltype(&T::a)>
{ test(){std::cout<< "Using A::a" <<std::endl;} };
int main()
{ test<A> a; }
Output by both compilers:
Using None
If the previous explanation is true, then why doesn't g++ prompt an error when the private member function is used? Again, this is only an assumption based on the outputs, but I think that this bit actually works as it should. Long story short, SFINAE kicks in, the specialization is discarded from the overload set, and only the first template gets instantiated. Maybe there's more to it than meets the eye, but if we explicitly specify the second template argument, both compilers will prompt the same error.
int main()
{ test<A , decltype(&A::a)> b; }
Output by both compilers:
error: ‘void A::a()’ is private
Anyway, this is the final code I've been using. To demonstrate the outputs, I've made the class public. As an interesting event, I've added a nullptr to point to the member function directly. The type from decltype(((T*)nullptr)->f()) is void, and from the example below, a and c are both invoked by the specialization rather than the first template. The reason is because the second template is more specialized than the first and therefore is the best match for both of them (kill two birds with one stone) (Template Formal Ordering Rules : https://stackoverflow.com/a/9993549/2754510). The type from decltype(&T::f) is M4GolfFvvE (possible translation: Men 4 Golf Fear very vicious Elk), which thanks to boost::typeindex::type_id_with_cvr, it is demangled into void (Golf::*)().
#include <iostream>
#include <boost/type_index.hpp>
class Golf
{
public:
int v;
void f()
{};
};
template<typename T>
using var_t = decltype(T::v);
template<typename T>
using func_t = decltype(&T::f);
//using func_t = decltype(((T*)nullptr)->f()); //void
template<typename, typename = void>
struct test
{
test(){std::cout<< "Using None" <<std::endl;}
};
template<typename T>
struct test<T,var_t<T> >
{
test(){std::cout<< "Using Golf::v" <<std::endl;}
};
template<typename T>
struct test<T,func_t<T> >
{
test(){std::cout<< "Using Golf::f" <<std::endl;}
};
int main()
{
test<Golf> a;
test<Golf,var_t<Golf> > b;
test<Golf,func_t<Golf> > c;
using boost::typeindex::type_id_with_cvr;
std::cout<< typeid(func_t<Golf>).name() << " -> " << type_id_with_cvr<func_t<Golf>>().pretty_name() <<std::endl;
}
Output from both compilers (func_t = decltype(&T::f)):
Using None
Using Golf::v
Using Golf::f
M4GolfFvvE -> void (Golf::*)()
Output from both compilers (func_t = decltype(((T*)nullptr)->f())):
Using Golf::f
Using Golf::v
Using Golf::f
v -> void
Edit:
According to #Dr.Gut (comment below) this bug continues to exist in gcc 9.2. However I found a "hack" around this using std::declval which makes it look even weirder.
#include <utility>
class A
{
int a;
};
template<typename, typename = void>
class test
{};
template<typename T>
class test<T,decltype(std::declval<A>().a)>
{};
int main()
{
test<A> a;
}
Online example: https://rextester.com/BUFU29474
The code compiles and runs fine in g++ and vc++ but fails in clang++.

Error when using template class as template template-parameter in member variable

I am using templates in my C++ project, and I'm having an issue when using a templated type as a template template-parameter. I think the best way to describe it is to give an example that produces the error:
template <template<class> class P, typename T>
class Foo {
P<T> baz;
};
template <class T>
class Bar {
Foo<Bar, T> memberFoo;
void makeFoo() {
Foo<Bar, T>* f = new Foo<Bar, T>();
}
};
Foo<Bar, int> globalFoo;
The declaration of globalFoo causes no error, but the declarations of memberFoo and f cause the compiler error:
error: template argument for template template parameter must be a class template
or type alias template
The error only occurs when using Bar as a template parameter inside of the declaration of the Bar class, but occurs using both clang and g++. This seems like something that would be documented somewhere, but googling yields no SO questions or other documentation.
Is this use of templates simply not legal in C++, or am I misunderstanding something about how to define and use templates? If this design architecture is not allowed by the C++11 standard, what is a workaround I can use?
The issue is that an incomplete type is yielded at time of instantiation. Clang and G++ yield different errors because Clang (apparently) doesn't implement a C++11 rule that the injected class name can refer to the class template itself when used as a template template parameter (Igor's suggestion doesn't work btw.) Changing Bar to ::Bar fixes this error, which makes Clang point out the incomplete error like G++ does. Changing baz to P<T>* allows it to compile.
N.B. Even though it compiles, it probably is undefined behavior. I suggest redesigning your classes.
Based on #Igor 's comment, I have figured out a couple of workarounds for this issue. Are based on the fact that referencing Bar within its declaration refers to this specialization of Bar (to quote #Igor).
Note that all of these workarounds rely on the fact that baz can be declared as a pointer. baz not being a pointer causes a recursion issue that was mentioned by #Nir in the comments and leads to the error:
field has incomplete type 'Foo<Bar<int>, int>'
Workaround 1
Add a forward declaration of Bar and create a template alias Bar2. For this to compile, baz must be a pointer:
template <template <typename> class P, typename T>
class Foo {
P<T>* baz;
};
template<typename T> class Bar;
template <typename U> using Bar2 = Bar<U>;
template <class T>
class Bar {
Foo<Bar2, T> memberFoo;
void makeFoo() {
Foo<Bar2, T>* f = new Foo<Bar2, T>();
}
};
Foo<Bar, int> globalFoo;
The forward declaration and use of a template alias forces the compiler to use the unspecialized version of Bar when defining memberFoo and f, whereas it defaults to using the unspecialized version in the definition of globalFoo.
Workaround 2
This workaround is based on #user5800314's answer. I feel no need to re-state his workaround, but I do feel that it is worth noting the reason why it works.
I have read a similar SO question about injected class names and C++11, but the important difference here is that my code does not compile on g++, whereas theirs does. I do not believe that the issue is a lack of implementation of injected class names. I believe that this workaround fixes the compilation error because using ::Bar instead of Bar again forces the compiler to access the global (unspecialized) version of Bar instead of accessing the local (specialized) version of Bar.
Workaround 3
Specify Foo as having a class (or typename) template-parameter instead of a template template-parameter, and be explicit about which specialization is being used whenever using the Foo template. This also requires that baz is a pointer, and that it does not use a template type:
template <class P, typename T>
class Foo {
P* baz;
};
template <class T>
class Bar {
Foo<Bar, T> memberFoo;
void makeFoo() {
Foo<Bar, T>* f = new Foo<Bar, T>();
}
};
Foo<Bar<int>, int> globalFoo;
This workaround resolves the of potential confusion of template template-parameters by requiring that a specific class is provided to the Foo template. This workaround may not be usable in some cases, but may be an elegant solution in others. In my case, for instance, I will not need to instantiate an instance of Foo from outside of Bar, so this is a very nice way of getting around the compile error.
P.S. I really would like to credit #user5800314, since his workaround does work, however I provide a different explanation here, and since the explanation I provide here is what I believe is correct, I don't feel that I can mark #user5800314 's answer as accepted.

Does a SFINAEd-out function shadows an explicitly imported overload from the base class

After having hitten problems with another design, I decided make make a wrapper class to add overloads to a some member functions of the base class if and only if viable overloads do not already exist in the base class. Basically, here is what I am trying to do:
template<typename T>
struct wrapper: T
{
using T::foo;
template<typename Arg>
auto foo(Arg) const
-> std::enable_if_t<not std::is_constructible<Arg>::value, bool>
{
return false;
}
};
struct bar
{
template<typename Arg>
auto foo(Arg) const
-> bool
{
return true;
}
};
In this simple example, wrapper adds an overloaded foo only if the one from the base class is not viable (I simplified the std::enable_if to the simplest possible thing; the original one involved the detection idiom). However, g++ and clang++ disagree. Take the following main:
int main()
{
assert(wrapper<bar>{}.foo(0));
}
g++ is ok with it: the foo from wrapper<bar> is SFINAEd out so it uses the one from bar instead. On the other hand, clang++ seems to assume that wrapper<bar>::foo always shadows bar::foo, even when SFINAEd out. Here is the error message:
main.cpp:30:26: error: no matching member function for call to 'foo'
assert(wrapper<bar>{}.foo(0));
~~~~~~~~~~~~~~~^~~
/usr/include/assert.h:92:5: note: expanded from macro 'assert'
((expr) \
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/type_traits:2388:44: note: candidate template ignored: disabled by 'enable_if' [with Arg = int]
using enable_if_t = typename enable_if<_Cond, _Tp>::type;
^
1 error generated.
So, who is right? Should this code be rejected just like clang++ does, or should it work and call bar::foo?
Consider §10.2:
In the declaration set, using-declarations are replaced by the set of
designated members that are not hidden or overridden by members of the
derived class (7.3.3),
And §7.3.3 goes
When a using-declaration brings names from a base class into a derived class scope, […] member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list (8.3.5 [dcl.fct]), cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting).
Clearly, the only difference in your example lies within the return types. Thus Clang is correct and GCC is bugged.
The wording was introduced by CWG #1764:
According to 7.3.3 [namespace.udecl] paragraph 15,
When a using-declaration brings names from a base class into a derived class scope, […]
The algorithm for class-scope name lookup given in 10.2
[class.member.lookup], however, does not implement this requirement;
there is nothing that removes a hidden base class member (replacing
the using-declaration, per paragraph 3) from the result set.
The resolution was moved to DR in February 2014, so perhaps GCC didn't implement it yet.
As mentioned in #TartanLlama's answer, you can introduce a counterpart to handle the other case. Something along the lines of
template <typename Arg, typename=std::enable_if_t<std::is_constructible<Arg>{}>>
decltype(auto) foo(Arg a) const
{
return T::foo(a);
}
Demo.
This seems to be a bug; with the explicit using declaration, Clang has to consider the base overload. There's no reason for it not to.
Your code isn't correct, though, because there's no reason to prefer the restricted version to the base version when both are valid, so I believe you should get an ambiguity error if you pass something that isn't default-constructible.
As #SebastianRedl says, this looks like a clang bug (Edit: looks like we were wrong).
Regardless of which compiler is correct, a possible workaround would be to define two versions of wrapper<T>::foo: one for when T::foo does not exist, which provides a custom implementation; and one for when it does, which forwards the call to the base version:
template<typename T>
struct wrapper: T
{
template<typename Arg, typename U=T>
auto foo(Arg) const
-> std::enable_if_t<!has_foo<U>::value, bool>
{
return false;
}
template<typename Arg, typename U=T>
auto foo(Arg a) const
-> std::enable_if_t<has_foo<U>::value, bool>
{
return T::foo(a); //probably want to perfect forward a
}
};
Live Demo