Ambigous operator overload on clang [duplicate] - c++

Consider the following code, which mixes a member and a non-member operator|
template <typename T>
struct S
{
template <typename U>
void operator|(U)
{}
};
template <typename T>
void operator|(S<T>,int) {}
int main()
{
S<int>() | 42;
}
In Clang this code fails to compile stating that the call to operator| is ambiguous, while gcc and msvc can compile it. I would expect the non-member overload to be selected since it is more specialized. What is the behavior mandated by the standard? Is this a bug in Clang?
(Note that moving the operator template outside the struct resolves the ambiguity.)

I do believe clang is correct marking the call as ambiguous.
Converting the member to a free-standing function
First, the following snippet is NOT equal to the code you have posted w.r.t S<int>() | 42; call.
template <typename T>
struct S
{
};
template <typename T,typename U>
void operator|(S<T>,U)
{}
template <typename T>
void operator|(S<T>,int) {}
In this case the now-non-member implementation must deduce both T and U, making the second template more specialized and thus chosen. All compilers agree on this as you have observed.
Overload resolution
Given the code you have posted.
For overload resolution to take place, a name lookup is initiated first. That consists of finding all symbols named operator| in the current context, among all members of S<int>, and the namespaces given by ADL rules which is not relevant here. Crucially, T is resolved at this stage, before overload resolution even happens, it must be. The found symbols are thus
template <typename T> void operator|(S<T>,int)
template <typename U> void S<int>::operator|(U)
For the purpose of picking a better candidate, all member functions are treated as non-members with a special *this parameter. S<int>& in our case.
[over.match.funcs.4]
For implicit object member functions, the type of the implicit object parameter is
(4.1)
“lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier
(4.2)
“rvalue reference to cv X” for functions declared with the && ref-qualifier
where X is the class of which the function is a member and cv is the cv-qualification on the member function declaration.
This leads to:
template <typename T> void operator|(S<T>,int)
template <typename U> void operator|(S<int>&,U)
Looking at this, one might assume that because the second function cannot bind the rvalue used in the call, the first function must be chosen. Nope, there is special rule that covers this:
[over.match.funcs.5][Emphasis mine]
During overload resolution, the implied object argument is indistinguishable from other arguments.
The implicit object parameter, however, retains its identity since no user-defined conversions can be applied to achieve a type match with it.
For implicit object member functions declared without a ref-qualifier, even if the implicit object parameter is not const-qualified, an rvalue can be bound to the parameter as long as in all other respects the argument can be converted to the type of the implicit object parameter.
Due to some other rules, U is deduced to be int, not const int& or int&. S<T> can also be easily deduced as S<int> and S<int> is copyable.
So both candidates are still valid. Furthermore neither is more specialized than the other. I won't go through the process step by step because I am not able to who can blame me if even all the compilers did not get the rules right here. But it is ambiguous exactly for the same reason as foo(42) is for
void foo(int){}
void foo(const int&){}
I.e. there is no preference between copy and reference as long as the reference can bind the value. Which is true in our case even for S<int>& due to the rule above. The resolution is just done for two arguments instead of one.

Related

Behaviour of friend function template returning deduced dependent type in class template

I've happened across the following code, the behaviour of which is diagreed upon by all of GCC, Clang, and MSVC:
#include <concepts>
template<typename T>
auto foo(T);
template<typename U>
struct S {
template<typename T>
friend auto foo(T) {
return U{};
}
};
S<double> s;
static_assert(std::same_as<decltype(foo(42)), double>);
Live demo: https://godbolt.org/z/hK6xhesKM
foo() is declared at global namespace with deduced return type. S<U> provides a definition for foo() via a friend function, where it returns a value of type U.
What I expected is that when S is instantiated with U=double, its definition of foo() gets put in the global namespace and U is substituted in, due to how friend functions work, effectively like this:
template<typename T>
auto foo(T);
S<double> s;
template<typename T>
auto foo(T) {
return double{};
}
Thus I expect foo()'s return type is double, and the following static assertion should pass:
static_assert(std::same_as<decltype(foo(42)), double>);
However, what actually happens is that all three compilers disagree on the behaviour.
GCC passes the static assertion, like I expect.
Clang fails the static assertion, as it seems to believe that foo() returns int:
'std::is_same_v<int, double>' evaluated to false
And MSVC produces a different error entirely:
'U': undeclared identifier
I find it bizarre that all the compilers have different behaviour here for a seemingly simple code example.
The issue does not occur if foo() isn't templated (see demo here), or if it doesn't have deduced return type (see demo here).
Which compiler has the correct behaviour? Or, is the code ill-formed NDR or undefined behaviour? (And, why?)
Which compiler has the correct behaviour? Or, is the code ill-formed NDR or undefined behaviour? (And, why?)
As #Sedenion points out in a comment, whilst touching upon the domain of CWG 2118 (we'll return to this further down) this program is by the current standard well-formed and GCC is correct to accept it, whereas Clang and MSVC are incorrect to reject it, as governed by [dcl.spec.auto.general]/12 and /13 [emphasis mine]:
/12 Return type deduction for a templated entity that is a function or function template with a placeholder in its declared type
occurs when the definition is instantiated even if the function body contains a return statement with a non-type-dependent operand.
/13 Redeclarations or specializations of a function or function template
with a declared return type that uses a placeholder type shall also
use that placeholder, not a deduced type. Similarly, redeclarations or
specializations of a function or function template with a declared
return type that does not use a placeholder type shall not use a
placeholder.
[Example 6:
auto f();
auto f() { return 42; } // return type is int
auto f(); // OK
// ...
template <typename T> struct A {
friend T frf(T);
};
auto frf(int i) { return i; } // not a friend of A<int>
Particularly the "not a friend of A<int>" example of /13 highlights that the redeclaration shall use a placeholder type (frf(T), and not a deduced type (frf(int)), whereas otherwise that particular example would be valid.
/12, along with [temp.inst]/3 (below) covers the that return type deduction for the friend occurs only after the friend's primary template definition has been made available (formally: it's declaration but not definition has been instantiated) from the instantiation of the S<double> enclosing class template specialization.
The implicit instantiation of a class template specialization causes
(3.1) the implicit instantiation of the declarations, but not of the definitions, of the non-deleted class member functions, member
classes, scoped member enumerations, static data members, member
templates, and friends; and
[...]
However, for the purpose of determining whether an instantiated
redeclaration is valid according to [basic.def.odr] and [class.mem], a
declaration that corresponds to a definition in the template is
considered to be a definition.
[Example 4:
// ...
template<typename T> struct Friendly {
template<typename U> friend int f(U) { return sizeof(T); }
};
Friendly<char> fc;
Friendly<float> ff; // error: produces second definition of f(U)
— end example]
CWG 2118: Stateful metaprogramming via friend injection
As covered in detail e.g. in A foliage of folly, one can rely on the single first instantiation of a class template to control how the definition of a friend looks like. First here means essentially relying on meta-programming state to specify this definition.
In OP's example the first instantiation, S<double>, is used to set the definition of the primary template of the friend foo such that its deduced type will always deduce to double, for all specializations of the friend. If we ever (in the whole program) instantiate, implicitly or explicitly, a second instantiation of S (ignoring S being specialized to remove the friend), we run into ODR-violations and undefined behavior. This means that this kind of program is, in practice, essentially useless, as it would serve clients undefined behavior on a platter, but as covered in the article linked to above it can be used for utility classes to circumvent private access rules (whilst still being entirely well-formed) or other hacky mechanisms such as stateful metaprogramming (which typically runs into or beyond the grey area of well-formed).

Ambiguous operator overload in Clang

Consider the following:
template<typename T>
struct C {};
template<typename T, typename U>
void operator +(C<T>&, U);
struct D: C<D> {};
struct E {};
template<typename T>
void operator +(C<T>&, E);
void F() { D d; E e; d + e; }
This code compiles fine on both GCC-7 and Clang-5. The selected overload for operator + is that of struct E.
Now, if the following change takes place:
/* Put `operator +` inside the class. */
template<typename T>
struct C {
template<typename U>
void operator +(U);
};
that is, if operator + is defined inside the class template, instead of outside, then Clang yields ambiguity between both operator +s present in the code. GCC still compiles fine.
Why does this happen? Is this a bug in either GCC or Clang?
This is a bug in gcc; specifically, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53499 .
The problem is that gcc is regarding the implicit object parameter of a class template member function as having a dependent type; that is, during function template partial ordering gcc transforms
C<D>::template<class U> void operator+(U); // #1
into
template<class T, class U> void operator+(C<T>&, U); // #1a (gcc, wrong)
when it should be transformed into
template<class U> void operator+(C<D>&, U); // #1b (clang, correct)
We can see that when compared to your
template<class T> void operator+(C<T>&, E); // #2
#2 is better than the erroneous #1a, but is ambiguous with #1b.
Observe that gcc incorrectly accepts even when C<D> is not a template at all - i.e., when C<D> is a class template full specialization:
template<class> struct C;
struct D;
template<> struct C<D> {
// ...
This is covered by [temp.func.order]/3, with clarification in the example. Note that again, gcc miscompiles that example, incorrectly rejecting it but for the same reason.
Edit: The original version of this answer said that GCC was correct. I now believe that Clang is correct according to the wording of the standard, but I can see how GCC's interpretation could also be correct.
Let's look at your first example, where the two declarations are:
template<typename T, typename U>
void operator +(C<T>&, U);
template<typename T>
void operator +(C<T>&, E);
Both are viable, but it is obvious that the second template is more specialized than the first. So GCC and Clang both resolve the call to the second template. But let's walk through [temp.func.order] to see why, in the wording of the standard, the second template is more specialized.
The partial ordering rules tell us to replace each type template parameter with a unique synthesized type and then perform deduction against the other template. Under this scheme, the first overload type becomes
void(C<X1>&, X2)
and deduction against the second template fails since the latter only accepts E. The second overload type becomes
void(C<X3>&, E)
and deduction against the first template succeeds (with T = X3 and U = E). Since the deduction succeeded in only one direction, the template that accepted the other's transformed type (the first one) is considered less specialized, and thus, the second overload is chosen as the more specialized one.
When the second overload is moved into class C, both overloads are still found and the overload resolution process should apply in exactly the same way. First, the argument list is constructed for both overloads, and since the first overload is a non-static class member, an implied object parameter is inserted. According to [over.match.funcs], the type of that implied object parameter should be "lvalue reference to C<T>" since the function does not have a ref-qualifier. So the two argument lists are both (C<D>&, E). Since this fails to effect a choice between the two overloads, the partial ordering test kicks in again.
The partial ordering test, described in [temp.func.order], also inserts an implied object parameter:
If only one of the function templates M is a non-static member of
some class A, M is considered to have a new first parameter inserted in its function parameter list. Given cv
as the cv-qualifiers of M (if any), the new parameter is of type “rvalue reference to cv A” if the optional
ref-qualifier of M is && or if M has no ref-qualifier and the first parameter of the other template has rvalue
reference type. Otherwise, the new parameter is of type “lvalue reference to cv A”. [ Note: This allows a
non-static member to be ordered with respect to a non-member function and for the results to be equivalent
to the ordering of two equivalent non-members. — end note ]
This is the step where, presumably, GCC and Clang take different interpretations of the standard.
My take: The member operator+ has already been found in the class C<D>. The template parameter T for the class C is not being deduced; it is known because the name lookup process entered the concrete base class C<D> of D. The actual operator+ that is submitted to partial ordering therefore does not have a free T parameter; it is not void operator+(C<T>&, U), but rather, void operator+(C<D>&, U).
Thus, for the member overload, the transformed function type should not be void(C<X1>&, X2), but rather void(C<D>&, X2). For the non-member overload, the transformed function type is still void(C<X3>&, E) as before. But now we see that void(C<D>&, X2) is not a match for the non-member template void(C<T>&, E) nor is void(C<X3>&, E) a match for the member template void(C<D>&, U). So partial ordering fails, and overload resolution returns an ambiguous result.
GCC's decision to continue to select the non-member overload makes sense if you assume that it is constructing the transformed function type for the member lexically, making it still void(C<X1>&, X2), while Clang substitutes D into the template, leaving only U as a free parameter, before beginning the partial ordering test.

What are the rules for choosing between a variadic template method and a usual template method?

I have two template operators in class:
template<class T>
size_t operator()(const T& t) const {
static_assert(boost::is_pod<T>(), "Not a POD type");
return sizeof t;
}
template<typename... T>
size_t operator()(const boost::variant<T...>& t) const
{
return boost::apply_visitor(boost::bind(*this, _1), t);
}
I pass boost::variant<some, pod, types, here> as an argument to these operators. GCC 4.8 and llvm 6.0 compile the code fine, choosing boost::variant parameterized operator. gcc 4.7 chooses const T& t parameterized operator and thus fails to compile due to static assert.
So, I have a question, what are the rules for choosing between these two?
I think gcc 4.7 must have a bug, but I don't have any proof.
The key section is in [temp.deduct.partial]:
Two sets of types are used to determine the partial ordering. For each of the templates involved there is
the original function type and the transformed function type. [ Note: The creation of the transformed type
is described in 14.5.6.2. —end note ] The deduction process uses the transformed type as the argument
template and the original type of the other template as the parameter template. This process is done twice
for each type involved in the partial ordering comparison: once using the transformed template-1 as the
argument template and template-2 as the parameter template and again using the transformed template-2
as the argument template and template-1 as the parameter template.
That's really dense, even for the C++ standard, but what it basically means is this. Take our two overloads:
template <class T> // #1
size_t operator()(const T& t) const
template <typename... T> // #2
size_t operator()(const boost::variant<T...>& t)
And we're going to basically assign some unique type(s) to each one and try to see if the other applies. So let's pick some type A for the #1, and B,C,D for #2. Does operator()(const A&) work for #2? No. Does operator()(const boost::variant<B,C,D>&) work for #1? Yes. Thus, the partial ordering rules indicate #2 is more specialized than #1.
And so, from [temp.func.order]:
The deduction process determines whether one of the templates is more specialized than the other. If
so, the more specialized template is the one chosen by the partial ordering process.
And from [over.match.best]:
[A] viable function F1 is defined to be a better function than another viable function
F2 if
— [..]
— F1 and F2 are function template specializations, and the function template for F1 is more specialized
than the template for F2 according to the partial ordering rules described in 14.5.6.2.
Thus, #2 should be chosen in any case where it applies. If GCC chooses #1, that is nonconforming behavior and is a bug.
In general the compiler just treats all deduced template instantiations as potential overloads, picking the "best viable function" (§ 13.3.3).
Indeed this means GCC 4.7 has a bug then.
See §14.8.3: Overload resolution
describes that all template instances will join in the set of candidates as any non-template declared overload:
A function template can be overloaded either by (non-template) functions of its
name or by (other) function templates of the same name. When a call to that
name is written (explicitly, or implicitly using the operator notation),
template argument deduction (14.8.2) and checking of any explicit template
arguments (14.3) are performed for each function template to find the template
argument values (if any) that can be used with that function template to
instantiate a function template specialization that can be invoked with the
call arguments. For each function template, if the argument deduction and
checking succeeds, the template- arguments (deduced and/or explicit) are used
to synthesize the declaration of a single function template specialization
which is added to the candidate functions set to be used in overload
resolution. If, for a given function template, argument deduction fails, no
such function is added to the set of candidate functions for that template. The
complete set of candidate functions includes all the synthesized declarations
and all of the non-template overloaded functions of the same name. The
synthesized declarations are treated like any other functions in the remainder
of overload resolution, except as explicitly noted in 13.3.3.
In the case of your question, the overloads end up being indistinguishable (credit: #Piotr S). In such cases "partial ordering" is applied (§14.5.6.2):
F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2
Note that things can get pretty tricky, when e.g. the "open template" version took a T& instead of T const& (non const references are preferred, all else being equal).
When you had several overloads that end up having the same "rank" for overload resolution, the call is ill-formed and the compiler will diagnose an ambiguous function invocation.

Template constructor vs. non-template constructor in class any

Imagine writing something like boost::any:
class any {
public:
any();
any(const any &);
any(any &&);
template<typename ValueType> any(const ValueType &);
template<typename ValueType> any(ValueType &&);
Will the appropriate (copy/move) constructor be called for any possible any? Or does it have to be written with SFINAE e.g. like this:
template<typename ValueType,
typename = typename std::enable_if<
!std::is_same<any, typename std::decay<ValueType>::type>::value
>::type>
any(const ValueType& value)
template<typename ValueType,
typename = typename std::enable_if<
!std::is_same<any, typename std::decay<ValueType>::type>::value
>::type>
any(ValueType&& value)
The question is: Do I need to protect the templated constructor (to construct any from some value) or I can leave it, because the non-template (copy/move) constructor will always be matched for any? What about volatile modifier or some strange std::move((const any&)it) if that is possible?
Answer describing the search for the constructor will be most appriciated, thank you.
EDIT: Constructing any containing another any would be a problem, I definitelly want to avoid that (the SFINAE makes sure it cannot happen).
With C++11 and the introduction of Universal Reference (and a constructor with such parameter) the rules of overload resolution will choose the templated version.
The truth is that if the compiler can choose between templated and non-templated function, it will go with the non-template. But it will do so only if they are equally good:
§ 13.3.3 Best viable function [over.match.best]
[...] Given these definitions, a viable function F1 is defined to be a better function than another viable function
F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
— for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,
[...]
— F1 is a non-template function and F2 is a function template specialization, [...]
That said, having two constructors declared like below:
any(const any &);
template <typename ValueType>
any(const ValueType &);
the compiler will choose the non-templated version, as instantiating the templated one would result with exactly same declaration.
However, with the constructor taking Unviersal Reference the situation changes radically:
any(const any &);
template <typename ValueType>
any(ValueType &&);
In the context of to copying an instance with regular direct-initialization syntax:
any a;
any b{a};
the evaluated type of a is an lvalue any & without the const modifier. After generating the set of candidate constructors for overload resolution the compiler ends up with following signatures:
any(const any &); // non-template
any(any &); // instantiated template
And then:
§ 13.3.1 Candidate functions and argument lists [over.match.funcs]
In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction (14.8.3, 14.8.2). Those candidates are then handled as candidate functions in the usual way. A given name can refer to one or more function templates and also to a set of overloaded non-template functions. In such a case, the candidate functions generated from each function template are combined with the set of non-template candidate functions.
That is, the template version is a better match, and this is what the compiler chooses.
However, if one had:
const any a; // const!
any b{a};
Then this time the constructor signature generated from constructor taking Universal Reference would be the same as the non-template version of copy-constructor, so only then the non-template version is called.
What about volatile modifier or some strange std::move((const any&)it) if that is possible?
Exactly the same happens. The Universal Reference constructor is a better match.
That is, std::move((const any&)it) evaluates to expression of const any && type.
The parameter of non-template move constructor can take non-const rvalue reference (so does not match at all, since it lacks const modifier).
The parameter of non-template copy constructor can take the const lvalue reference (that is fine, const rvalue can be bound by const lvalue reference, but is not an exact match).
Then, the instantiated template taking Universal Reference is again a better match that will be invoked.
As a general rule, if a template and a non-template function are otherwise equally good matches, the non-template version is chosen over the template version. Since your any copy/move constructors are non-template, for rvalues or constant lvalues they take precedence over the template constructors.
However thanks due the special rules for rvalue reference templates, the deduced type for template<typename ValueType> any(ValueType &&); will be any& which is a better match. Therefore when copying a non-const lvalue, you'll call the templated constructor.
Therefore you need an SFINAE rule for that templated constructor, but not for the templated constructor taking an lvalue reference to const.

Type deduction for non-viable function templates

In his answer to this question and the comment section, Johannes Schaub says there's a "match error" when trying to do template type deduction for a function template that requires more arguments than have been passed:
template<class T>
void foo(T, int);
foo(42); // the template specialization foo<int>(int, int) is not viable
In the context of the other question, what's relevant is whether or not type deduction for the function template succeeds (and substitution takes place):
template<class T>
struct has_no_nested_type {};
// I think you need some specialization for which the following class template
// `non_immediate_context` can be instantiated, otherwise the program is
// ill-formed, NDR
template<>
struct has_no_nested_type<double>
{ using type = double; };
// make the error appear NOT in the immediate context
template<class T>
struct non_immediate_context
{
using type = typename has_no_nested_type<T>::type;
};
template<class T>
typename non_immediate_context<T>::type
foo(T, int) { return {}; }
template<class T>
bool foo(T) { return {}; }
int main()
{
foo(42); // well-formed? clang++3.5 and g++4.8.2 accept it
foo<int>(42); // well-formed? clang++3.5 accepts it, but not g++4.8.2
}
When instantiating the first function template foo for T == int, the substitution produces an invalid type not in the immediate context of foo. This leads to a hard error (this is what the related question is about.)
However, when letting foo deduce its template-argument, g++ and clang++ agree that no instantiation takes place. As Johannes Schaub explains, this is because there is a "match error".
Question: What is a "match error", and where and how is it specified in the Standard?
Altenative question: Why is there a difference between foo(42) and foo<int>(42) for g++?
What I've found / tried so far:
[over.match.funcs]/7 and [temp.over] seem to describe the overload resolution specifics for function templates. The latter seem to mandate the substitution of template parameters for foo.
Interestingly, [over.match.funcs]/7 triggers the process described in [temp.over] before checking for viability of the function template (specialization).
Similarly, type deduction does not to take into account, say, default function arguments (other than making them a non-deduced context). It seems not to be concerned with viability, as far as I can tell.
Another possibly important aspect is how type deduction is specified. It acts on single function parameters, but I don't see where the distinction is made between parameter types that contain / are dependent on template parameters (like T const&) and those which aren't (like int).
Yet, g++ makes a difference between explicitly specifying the template parameter (hard error) and letting them be deduced (deduction failure / SFINAE). Why?
What I've summarized is the process described at 14.8.2.1p1
Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below.
In our case, we have for P (T, int) and for A, we have (int). For the first pair of P/A, which is T against int, we can match T to int (by the process described in 14.8.2.5). But for the second "pair", we have int but have no counterpart. Thus deduction cannot be made for this "pair".
Thereby, by 14.8.2.5p2, "If type deduction cannot be done for any P/A pair, ..., template
argument deduction fails.".
You then won't ever come to the point where you substitute template arguments into the function template.
This can all probably described more precisely in the Standard (IMO), but I believe this is how one could implement things to match the actual behavior of Clang and GCC and it seems a reasonable interpretation of the Standardese.