C++ Template overload with enable_if: different behaviour with g++ and clang - c++

During resolution of an overload of a templated member function of a base class, I observed a different behaviour between g++ (5.2.1-23) and clang (3.8.0), with -std=c++14.
#include <iostream>
#include <type_traits>
struct Base
{
template <typename T>
auto a(T t) -> void {
std::cout<< "False\n";
}
};
template <bool Bool>
struct Derived : public Base
{
using Base::a;
template <typename T, bool B = Bool>
auto a(T t) -> std::enable_if_t<B, void>
{
std::cout<< "True\n";
}
};
int main()
{
Derived<true> d;
d.a(1); // fails with g++, prints "true" with clang
Derived<false> d2;
d2.a(1); // fails with clang++, prints "false" with g++
}
The call to Derived<true>::a fails with g++ with the following message:
test.cc: In function ‘int main()’:
test.cc:28:8: error: call of overloaded ‘a(int)’ is ambiguous
d.a(1);
^
test.cc:18:8: note: candidate: std::enable_if_t<B, void> Derived<Bool>::a(T) [with T = int; bool B = true; bool Bool = true; std::enable_if_t<B, void> = void]
auto a(T t) -> std::enable_if_t<B, void>
^
test.cc:7:8: note: candidate: void Base::a(T) [with T = int]
auto a(T t) -> void {
^
and the call to Derived<false>::a fails with clang++ with the following message:
test.cc:32:6: error: no matching member function for call to 'a'
d2.a(1);
~~~^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.2.1/../../../../include/c++/5.2.1/type_traits:2388:44: note: candidate template ignored: disabled by 'enable_if' [with T = int, B = false]
using enable_if_t = typename enable_if<_Cond, _Tp>::type;
^
My guess is that they interpret differently the using Base::a;, and that it isn't considered in clang, whereas it's (maybe too much) considered in g++. What I thought would happen is that if Derived has true as parameter, then the call of a() is dispatched to Derived's implementation, whereas if the parameter is false, the call is dispatched to Base::a.
Are they both wrong? Who is right? Who should I submit a bug report to? Can somebody explain what is going on?
Thanks

From 3.3.10/p3 Name hiding [basic.scope.hiding]:
In a member function definition, the declaration of a name at block
scope hides the declaration of a member of the class with the same
name; see 3.3.7. The declaration of a member in a derived class
(Clause 10) hides the declaration of a member of a base class of the
same name; see 10.2
Also 7.3.3/p15 The using declaration [namespace.udecl]:
When a using-declaration brings names from a base class into a derived
class scope, 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 (8.3.5),
cv-qualification, and ref-qualifier (if any) in a base class (rather
than conflicting). [ Note: For using-declarations that name a
constructor, see 12.9. — end note ] [Example:
struct B {
virtual void f(int);
virtual void f(char);
void g(int);
void h(int);
};
struct D : B {
using B::f;
void f(int); // OK: D::f(int) overrides B::f(int);
using B::g;
void g(char); // OK
using B::h;
void h(int); // OK: D::h(int) hides B::h(int)
};
void k(D* p)
{
p->f(1); // calls D::f(int)
p->f(’a’); // calls B::f(char)
p->g(1); // calls B::g(int)
p->g(’a’); // calls D::g(char)
}
— end example ]
This is resolved during member name look-up. Thus, it's before template argument deduction. Consequently, as correctly TC mentioned in the comments Base template function is hidden no matter of SFINAE verdict.
Therefore CLANG is correct and GCC is wrong.

Related

Using using-declarations to overload with base class, gcc accepts clang rejects [duplicate]

Microsoft compiler (Visual Studio 2017 15.2) rejects the following code:
#include <type_traits>
struct B
{
template<int n, std::enable_if_t<n == 0, int> = 0>
void f() { }
};
struct D : B
{
using B::f;
template<int n, std::enable_if_t<n == 1, int> = 0>
void f() { }
};
int main()
{
D d;
d.f<0>();
d.f<1>();
}
The error is:
error C2672: 'D::f': no matching overloaded function found
error C2783: 'void D::f(void)': could not deduce template argument for '__formal'
note: see declaration of 'D::f'
Clang also rejects it:
error: no matching member function for call to 'f'
d.f<0>();
~~^~~~
note: candidate template ignored: disabled by 'enable_if' [with n = 0]
using enable_if_t = typename enable_if<_Cond, _Tp>::type;
GCC perfectly accepts it. Which compiler is right?
Addition:
With SFINAE in the form
template<int n, typename = std::enable_if_t<n == 0>>
...
template<int n, typename = std::enable_if_t<n == 1>>
GCC also produces an error:
error: no matching function for call to ‘D::f<0>()’
d.f<0>();
^
note: candidate: template<int n, class> void D::f()
void f()
^
note: template argument deduction/substitution failed:
Turning cppleaner's comment into an answer:
From namespace.udecl#15.sentence-1:
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)
Unfortunately, template parameter doesn't count and both f has empty parameter-type-list, are not const and no ref-qualifier.
Derived::f so hides Base::f.
gcc is wrong to accept that code.
So the way to fix it is by default argument (returned type doesn't count either):
struct B
{
template <int n>
void f(std::enable_if_t<n == 0>* = nullptr) { }
};
struct D : B
{
using B::f;
template <int n>
void f(std::enable_if_t<n == 1>* = nullptr) { }
};

Error when passing template parameters to inner structure

I try to compile the following C++ code:
struct A {
template< bool x >
bool fun() {
return x;
}
};
template< typename T >
struct B {
struct A2 {
template< bool x >
bool fun() {
return x;
}
};
void test() {
A a;
A2 a2;
a.fun< true >();
a2.fun< true >();
}
};
The compiler complains that:
source_file.cpp: In member function ‘void B<T>::test()’:
source_file.cpp:22:24: error: expected primary-expression before ‘)’ token
a2.fun< true >();
^
However the line just above (a.fun< true >()) compiles just fine. Interestingly, if I remove the line template< typename T >, then compilation succeeds. However that line is required for reasons that do not appear from the minimal (not) working example. What is wrong here?
In this context A2 is actually a shorthand for B<T>::A2. Since A2 is a type dependent on the template parameter T, you have to refer to its member templates with an explicit template keyword
a2.template fun< true >();
A is not a dependent type and its member templates can be referred to with "plain" syntax without that extra template keyword.
See 14.2/4 and a similar example there
14.2 Names of template specializations [temp.names]
4 When the name of a member template specialization appears after . or -> in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (14.6.2.1), the member template name must be prefixed by the keyword template.
Otherwise the name is assumed to name a non-template. [ Example:
struct X {
template<std::size_t> X* alloc();
template<std::size_t> static X* adjust();
};
template<class T> void f(T* p) {
T* p1 = p->alloc<200>(); // ill-formed: < means less than
T* p2 = p->template alloc<200>(); // OK: < starts template argument list
T::adjust<100>(); // ill-formed: < means less than
T::template adjust<100>(); // OK: < starts template argument list
}
—end example ]

Overload resolution between template members in base and derived classes

Microsoft compiler (Visual Studio 2017 15.2) rejects the following code:
#include <type_traits>
struct B
{
template<int n, std::enable_if_t<n == 0, int> = 0>
void f() { }
};
struct D : B
{
using B::f;
template<int n, std::enable_if_t<n == 1, int> = 0>
void f() { }
};
int main()
{
D d;
d.f<0>();
d.f<1>();
}
The error is:
error C2672: 'D::f': no matching overloaded function found
error C2783: 'void D::f(void)': could not deduce template argument for '__formal'
note: see declaration of 'D::f'
Clang also rejects it:
error: no matching member function for call to 'f'
d.f<0>();
~~^~~~
note: candidate template ignored: disabled by 'enable_if' [with n = 0]
using enable_if_t = typename enable_if<_Cond, _Tp>::type;
GCC perfectly accepts it. Which compiler is right?
Addition:
With SFINAE in the form
template<int n, typename = std::enable_if_t<n == 0>>
...
template<int n, typename = std::enable_if_t<n == 1>>
GCC also produces an error:
error: no matching function for call to ‘D::f<0>()’
d.f<0>();
^
note: candidate: template<int n, class> void D::f()
void f()
^
note: template argument deduction/substitution failed:
Turning cppleaner's comment into an answer:
From namespace.udecl#15.sentence-1:
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)
Unfortunately, template parameter doesn't count and both f has empty parameter-type-list, are not const and no ref-qualifier.
Derived::f so hides Base::f.
gcc is wrong to accept that code.
So the way to fix it is by default argument (returned type doesn't count either):
struct B
{
template <int n>
void f(std::enable_if_t<n == 0>* = nullptr) { }
};
struct D : B
{
using B::f;
template <int n>
void f(std::enable_if_t<n == 1>* = nullptr) { }
};

ambiguous operator[] in variadic template

I'm trying to compile this example, where a variadic class template inherits from a variadic amount of bases, each of which implements a different operator[]:
#include <iostream>
template <typename T>
struct Field {
typename T::value_type storage;
typename T::value_type &operator[](const T &c) {
return storage;
}
};
template<typename... Fields>
struct ctmap : public Field<Fields>... {
};
int main() {
struct age { typedef int value_type; };
struct last_name { typedef std::string value_type; };
ctmap<last_name, age> person;
person[last_name()] = "Smith";
person[age()] = 104;
std::cout << "Hello World!" << std::endl;
return 0;
}
When I compile with gcc (Debian 4.9.2-10), I get the following error
main.cpp: In function ‘int main()’:
main.cpp:22:23: error: request for member ‘operator[]’ is ambiguous
person[last_name()] = "Smith";
^
main.cpp:7:27: note: candidates are: typename T::value_type& Field<T>::operator[](const T&) [with T = main()::age; typename T::value_type = int]
typename T::value_type &operator[](const T &c) {
^
main.cpp:7:27: note: typename T::value_type& Field<T>::operator[](const T&) [with T = main()::last_name; typename T::value_type = std::basic_string<char>]
main.cpp:23:17: error: request for member ‘operator[]’ is ambiguous
person[age()] = 104;
^
main.cpp:7:27: note: candidates are: typename T::value_type& Field<T>::operator[](const T&) [with T = main()::age; typename T::value_type = int]
typename T::value_type &operator[](const T &c) {
^
main.cpp:7:27: note: typename T::value_type& Field<T>::operator[](const T&) [with T = main()::last_name; typename T::value_type = std::basic_string<char>]
Why is this ambiguous?
A portable way do do what you want is roughly:
template<class...Ts>
struct operator_index_inherit {};
template<class T0, class T1, class...Ts>
struct operator_index_inherit<T0, T1, Ts...>:
T0, operator_index_inherit<T1, Ts...>
{
using T0::operator[];
using operator_index_inherit<T1, Ts...>::operator[];
};
template<class T0>
struct operator_index_inherit<T0>:
T0
{
using T0::operator[];
};
then:
template<class... Fields>
struct ctmap : operator_index_inherit<Field<Fields>...> {
using base = operator_index_inherit<Field<Fields>...>;
using base::operator[];
};
here we linearly inherit from each of the types, and using operator[] on our parents.
If we could using Field<Fields>::operator[]...; we would not have to do this.
Some care has to be taken with constructors (which I did not take), but you might not need to do this.
live example.
What is actually going wrong depends on details of the standard I am less than certain of. Basically, you are mixing operators and inheritance and overloading in a complex way. Even if your code is standard compliant (which it may or may not be), it is compliant in a way that some compilers die on.
The code is invalid and gcc is correct to reject it (clang 3.6.0 accepts it though - this is a bug). The rules for looking up an operator start with, from [over.match.oper]:
[...] for a binary
operator # with a left operand of a type whose cv-unqualified version is T1 and a right operand of a type
whose cv-unqualified version is T2, three sets of candidate functions, designated member candidates, non-member
candidates and built-in candidates, are constructed as follows:
— If T1 is a complete class type or a class currently being defined, the set of member candidates is the
result of the qualified lookup of T1::operator# (13.3.1.1.1); otherwise, the set of member candidates
is empty.
The lookup rules for a member name are (since we're looking up ctmap<last_name,age>::operator[]), from [class.member.lookup]:
The lookup set for f in C, called S(f,C), [...] is calculated
as follows:
If C contains a declaration of the name f, [...]
Otherwise (i.e., C does not contain a declaration of f or the resulting declaration set is empty), S(f,C) is
initially empty. If C has base classes, calculate the lookup set for f in each direct base class subobject Bi,
and merge each such lookup set S(f,Bi) in turn into S(f,C).
The following steps define the result of merging lookup set S(f,Bi) into the intermediate S(f,C):
— [...]
— Otherwise, if the declaration sets of S(f,Bi) and S(f,C) differ, the merge is ambiguous: the new
S(f,C) is a lookup set with an invalid declaration set and the union of the subobject sets. In subsequent
merges, an invalid declaration set is considered different from any other.
— [...]
Basically - we have two base classes here, both with an operator[]. Both declaration sets differ - so the merge is ambiguous. The way to disambiguate would be to introduce using-declarations bringing in all the base class member functions into the derived class, so that the initial lookup set finds everything.
To shorten your example:
struct A { void foo(char) { } };
struct B { void foo(int ) { } };
struct C : A, B { };
struct D : A, B {
using A::foo;
using B::foo;
};
With that hierarchy
C c;
c.foo(4); // error: ambiguous lookup set for foo()
D d;
d.foo('x') // OK: calls A::foo()

clang++ error on late default template parameter declaration

The following code compiles fine with g++, but not with clang++ (3.6):
// Forward declaration:
template <class S, class T>
struct Base;
template <class T>
struct BaseFriend {
friend struct Base<int, T>;
};
// Actual declaration:
template <class S, class T = int>
struct Base {
void foo() {}
};
struct DerivedFriend : BaseFriend<int> {};
struct Derived : Base<int> {
void foo(int) {
Base<int>::foo();
}
};
Error occurs in the Derived::foo definition:
error: too few template arguments for class template 'Base'
Base<int>::foo();
^
test.cpp:3:8: note: template is declared here
struct Base;
^
Error goes away after few minor fixes, like:
If default template parameter is defined in forward declaration instead of actual declaration.
Or if DerivedFriend is not used.
But, what is wrong with the original code?
Definitely a clang bug, looks like #10147. The standard clearly allows this [temp.param]/10:
The set of default template-arguments available for use with a template declaration or definition is obtained
by merging the default arguments from the definition (if in scope) and all declarations in scope in the same
way default function arguments are (8.3.6). [ Example:
template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;
is equivalent to
template<class T1 = int, class T2 = int> class A;
—end example ]