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.
Related
In the following program struct A has a constructor template A(T) requiring that the type T be copy-constructible. At the same time A itself must have implicitly defined copy-constructor:
#include <type_traits>
struct A {
template <class T>
A(T) requires(std::is_copy_constructible_v<T>) {}
};
static_assert(std::is_copy_constructible_v<A>);
and the last static_assert(std::is_copy_constructible_v<A>) passes in GCC and MSVC, but Clang rejects it, complaining:
error: substitution into constraint expression resulted in a non-constant expression
A(T) requires(std::is_copy_constructible_v<T>) {}
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/12.0.1/../../../../include/c++/12.0.1/type_traits:971:30: note: while checking constraint satisfaction for template 'A<A>' required here
: public __bool_constant<__is_constructible(_Tp, _Args...)>
^~~~~~~~~~~~~~~~~~
...
Demo: https://gcc.godbolt.org/z/shKe7W1jr
Is it just a Clang bug?
TLDR
Given the following examples A (OP's example), B and C:
struct A {
template <class T>
A(T) requires(!std::is_copy_constructible_v<T>) {}
};
static_assert(std::is_copy_constructible_v<A>); // #OP
struct B {
template <class T>
B(const T&) requires(!std::is_copy_constructible_v<T>) {}
};
static_assert(std::is_copy_constructible_v<B>); // #i
struct C {
template <class T>
C(T&&) requires(!std::is_copy_constructible_v<T>) {}
};
static_assert(std::is_copy_constructible_v<C>); // #ii
Then:
A with #OP is well-formed
[rejects-invalid] Clang is wrong to reject it
[accepts-valid] GCC and MVSC is correct to accept it
B with #i is arguably ill-formed due recursion during overload resolution
[rejects-valid] Clang is correct to reject it
[accepts-invalid] GCC and MVSC are arguably incorrect to accept it
C with #ii is well-formed
[accepts-valid] Clang, GCC and MVSC are correct to accept it
Details
First of all, as per [class.copy.ctor]/1 and [class.copy.ctor]/2 a template constructor is never a copy or a move constructor, respectively, meaning the rules about under which conditions move/copy constructors and assignment ops are implicitly declared, [class.copy.ctor]/6 and [class.copy.ctor]/8, are not affected by template constructors.
/1 A non-template constructor for class X is a copy constructor if
its first parameter is of type X&, const X&, volatile X& or const
volatile X&, and either there are no other parameters or else all
other parameters have default arguments ([dcl.fct.default])
/2 A non-template constructor for class X is a move constructor if
its first parameter is of type X&&, const X&&, volatile X&&, or const
volatile X&&, and either there are no other parameters or else all
other parameters have default arguments ([dcl.fct.default]).
/6 If the class definition does not explicitly declare a copy
constructor, a non-explicit one is declared implicitly. If the
class definition declares a move constructor or move assignment
operator, the implicitly declared copy constructor is defined as
deleted; otherwise, it is defined as defaulted ([dcl.fct.def]).
/8 If the definition of a class X does not explicitly declare a move
constructor, a non-explicit one will be implicitly declared as
defaulted if and only if
X does not have a user-declared copy constructor,
X does not have a user-declared copy assignment operator,
X does not have a user-declared move assignment operator, and
X does not have a user-declared destructor.
This means that OP's program may not be rejected as ill-formed because the class A is not copy constructible. That leaves the program being ill-formed due errors during overload resolution, particularly triggered by static_assert(std::is_copy_constructible_v<A>);, which as part of the trait will try to construct a type A with an argument of type A const&. Even if this is done in an unevaluated context, it will trigger overload resolution where all constructors of A, including the template constructor, are candidates. Simplified:
static_assert(std::is_copy_constructible_v<A>);
// overload res. for A obj("A const& arg")
// 1) candidates: a) copy ctor
// b) move ctor
// c) template ctor ?
// 2) viable candidates ?
As per [over.match.funcs]/7 the template ctor is a candidate
In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction ([temp.over], [temp.deduct]) [...]
However the resulting candidate function template specialization would be (the recursive constructor) A(A) and as per [class.copy.ctor]/5 a template constructor will never be used produce such a specialization:
/5 A declaration of a constructor for a class X is ill-formed if its
first parameter is of type cv X and either there are no other
parameters or else all other parameters have default arguments. A
member function template is never instantiated to produce such a
constructor signature.
Thus a specialization of the template constructor never even enters the set of candidate functions, meaning we never reach neither the state of rejecting candidates as per the usual overload resolution rules, nor the state of rejecting candidates due to failed constraints.
Thus, as overload resolution contains only the implicitly generated copy and move constructors, Clang is wrong to reject OP's program.
As pointed out by #Jarod42, a more interesting example is when the template constructor has an argument T const& or T&& (lvalue const reference and universal/forwarding reference, respectively):
struct B {
template <class T>
B(const T&) requires(!std::is_copy_constructible_v<T>) {}
};
static_assert(std::is_copy_constructible_v<B>); // #i
struct C {
template <class T>
C(T&&) requires(!std::is_copy_constructible_v<T>) {}
};
static_assert(std::is_copy_constructible_v<C>); // #ii
Curiosly, whilst GCC and MVSC accepts both these cases, Clang rejects #i with the same error messages as for OP's example, but accepts #ii.
For the template constructors of classes B and C, the special case of class.copy.ctor]/5 does not apply, meaning a specialization for both #i and #ii would enter the candidate set of the unevaluated call B obj("B const& arg") and C obj("C const& arg"), respectively.
Thus, these template constructors will enter the phase of finding viable candidates, at which point constraints will be checked, as per [over.match.viable]/3.
For B and its B(const T&) constructor, the candidate, including constraints, is
// T = B
B(const B&) requires requires(std::is_copy_constructible_v<B>)
Meaning that as part of checking the trait (recall the static assert)
std::is_copy_constructible_v<B>
we run into a constraint satisfaction check over the same trait, before completely resolving the first check, meaning recursion. I have not been able to find the explicit wording which would reject such a program, but it stands to reason that recursion during overload resolution should result in an ill-formed program. Thus, Clang is arguably correct to reject the B example.
For C and its C(T&&) constructor, the candidate, including constraints, is
// T = C const&
C(C const&) requires requires(std::is_copy_constructible_v<C const&>)
As opposed to the example of B, this does not lead to recursion as std::is_copy_constructible_v<C const&> is always true (true for any type C that is referenceable, e.g. anything but void or cv-/ref-qualified function types).
Thus, all compilers are arguably correct to accept the C example.
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.
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.
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.
I have 2 overloaded functions - one takes an L-value, and the other takes an R-value. The purpose is so that the function can be called like:
Obj obj;
foo(obj);
OR:
foo(Obj());
So, I write 2 overloaded functions:
template <class T>
void foo(T& v)
{
/* ... function body code goes here ... */
}
template <class T>
void foo(T&& v)
{
foo(v);
}
int main()
{
foo(int(5));
}
The R-value overload merely needs to delegate to the L-value overload. The way I understand it, once I'm in the body of the function, any use of v gives me an L-value reference, unless I specifically use std::move or std::forward. So calling foo(v) within the R-value overload should automatically call the L-value version (rather than recursing.)
But, the compiler complains about ambiguity:
test.cpp: In function ‘void foo(T&&) [with T = int]’:
test.cpp:305:12: instantiated from here
test.cpp:299:2: error: call of overloaded ‘foo(int&)’ is ambiguous
I don't understand why this is ambiguous. The call to foo() within the R-value overload should clearly call the L-value version. So why doesn't this compile?
Short version: Try updating your compiler. Your version doesn't implement http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1164 .
Your second template is a "perfect forwarding" template. Any function template parameter of type T&&, where T is a template parameter will deduce that template parameter to X (where X is the argument type) when the argument is an rvalue, and to X& if the argument is an lvalue.
In your case you passed an rvalue, so T was deduced to Obj (and int in your real code). If you would have passed a variable name or something else that is an lvalue, your second template would have the parameter type Obj& (T would be Obj&, and && applied to such a type stays Obj&).
But the other template also has such a parameter type. So during overload resolution the conversion of the argument to the parameter is the same (perfect match), and another criteria needs to inspected, the specifity of the two templates, under the partial ordering rule. If one template is more specialized than the other template, then it will be chosen by the compiler. If there is no template more specialized than the other one, a final ambiguity is risen.
In this case, the first template is more specialized than the second template, and hence the compiler should call the first template finally.