Overload resolution of constrained functions - c++

Is the call to f ambiguous? Since one of the functions is more constrained than
the other, I wouldn't expect there to be any ambiguity. Yet, GCC 12.2 and Clang trunk reject the
code, in contrast to Clang 15, MSVC 19.33, and MSVC trunk.
Code example:
template<typename...>
int f();
template<typename = void>
int f() requires true;
static_assert(sizeof(f()));
The error produced by GCC:
<source>:7:23: error: call of overloaded 'f()' is ambiguous
7 | static_assert(sizeof(f()));
| ~^~
<source>:2:5: note: candidate: 'int f() [with <template-parameter-1-1> = {}]'
2 | int f();
| ^
<source>:5:5: note: candidate: 'int f() requires true [with <template-parameter-1-1> = void]'
5 | int f() requires true;
| ^

Whether viable overloads from function templates are more constrained is considered only if the function templates are otherwise equivalent (except for potentially the return type). It is not sufficient that the call would otherwise be ambiguous.
In particular the corresponding template parameters must be equivalent, see [temp.func.order]/6.2.2. A template parameter that is a parameter pack (as in the first overload) is not equivalent to one that isn't (as in the second overload), see [temp.over.link]/6.2.
So the constraint doesn't matter for deciding which candidate is better. The call is ambiguous.

Related

Overloading static and non-static member function with constraint

Is this code valid?
template<bool b>
struct s {
void f() const {
}
static void f() requires b {
}
};
void g() {
s<true>().f();
}
clang says yes, but gcc says no
<source>: In function 'void g()':
<source>:10:20: error: call of overloaded 'f()' is ambiguous
10 | s<true>().f();
| ~~~~~~~~~~~^~
<source>:3:14: note: candidate: 'void s<b>::f() const [with bool b = true]'
3 | void f() const {
| ^
<source>:5:21: note: candidate: 'static void s<b>::f() requires b [with bool b = true]'
5 | static void f() requires b {
| ^
Compiler returned: 1
https://godbolt.org/z/f4Kb68aee
If we go through [over.match.best.general], we get:
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 [...]
The only argument is the object argument, and we have earlier that:
If F is a static member function, ICS1(F) is defined such that ICS1(F) is neither better nor worse than ICS1(G) for any function G, and, symmetrically, ICS1(G) is neither better nor worse than ICS1(F); otherwise,
So the premise holds: all arguments for one function have a conversion sequence no worse than the conversion sequence for the other function. So we move on to our tiebreakers...
for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,
The only argument that we could have a better conversion sequence for is the object argument, and as established, that one is equivalent. So this tiebreaker does not apply.
the context is an initialization by user-defined conversion (see [dcl.init], [over.match.conv], and [over.match.ref]) and [...]
Nope.
the context is an initialization by conversion function for direct reference binding of a reference to function type, [...]
Nope.
F1 is not a function template specialization and F2 is a function template specialization, or, if not that,
Nope.
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 [temp.func.order], or, if not that,
Nope.
F1 and F2 are non-template functions with the same parameter-type-lists, and F1 is more constrained than F2 according to the partial ordering of constraints described in [temp.constr.order], or if not that,
Aha! In this example, we have non-template functions with the same parameter-type-lists (both are just empty). The static member function is constrained and the non-static member function is not constrained, which is the most trivial kind of "more constrained" (see [temp.constr.order]).
As such, I think that clang (and msvc) are correct to accept the program and gcc is incorrect to reject it. (submitted 103783).
Your code is ill-formed according to C++20 standard class.static.mfct#2:
There shall not be a static and a non-static member function with the same name and the same parameter types ([over.load]).
There is no exception here for the presence of requires-clause to differentiate member functions, only same name and the same parameter types. And it is exactly our case: the same name is f, and the same parameter types is empty set.
So Clang and MSVC are wrong in accepting the code. But the diagnostics of GCC is definitely confusing.
With some minor tweaks in the code (removed const in not-static member function and get its address in the code), Clang and MSVC also show to have big problems with it:
template<bool b>
struct s {
void f() {}
static void f() requires b {}
};
int main() {
s<true>().f();
void (s<true>::*x)() = &s<true>::f;
}
Demo: https://gcc.godbolt.org/z/vdq9j63Gs

Deduce type of Compare for std::set from constructor arguments

I wanna write simpler syntax when declaring std::set with custom compare:
auto s = std::set({1,3,7,9,2,4},[](int a,int b){return a>b;});
but it does not work out of the box. CLang produces:
/Users/kyb/devel/untitled3/main.cpp:13:14: error: ambiguous deduction for template arguments of 'set'
auto s = set({1,3,7,9,2,4},[](int a,int b){return a>b;});
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/set:531:5: note: candidate function [with _Key = int, _Compare = (lambda at /Users/kyb/devel/untitled3/main.cpp:13:32), _Allocator = std::__1::allocator<int>]
set(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/set:547:5: note: candidate function [with _Key = int, _Compare = std::__1::less<int>, _Allocator = (lambda at /Users/kyb/devel/untitled3/main.cpp:13:32)]
set(initializer_list<value_type> __il, const allocator_type& __a)
^
1 error generated.
There is a trick - invade deduction guide:
namespace std::__1{
template<typename T, typename Compare> set(initializer_list<T> il, const Compare&comp) -> set<T,Compare>;
}
which produces warning:
warning: inline namespace reopened as a non-inline namespace
I believe there is a way to do that in more clean way.
clang --version:
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
/V/l/n/new-diag-commands ❯❯❯
I believe there is a way to do that in more clean way.
Your code snippet above should not be rejected by clang, as is governed by [associative.reqmts]/151 [emphasis mine]:
A deduction guide for an associative container shall not participate
in overload resolution if any of the following are true:
(15.1) It has an InputIterator template parameter and a type that does not qualify as an input iterator is deduced for that parameter.
(15.2) It has an Allocator template parameter and a type that does not qualify as an allocator is deduced for that parameter.
(15.3) It has a Compare template parameter and a type that qualifies as an allocator is deduced for that parameter.
As of Clang 9, this has been corrected.
[1] N4659: March 2017 post-Kona working draft/C++17 DIS.

Auto with uniform initialization expands to unexpected type

Consider this short program compiled with GCC 4.7.2 g++ -std=c++11 test.cc
#include <memory>
#include <queue>
struct type{
type(int a) : v(a) {}
int v;
};
typedef std::shared_ptr<type> type_ptr;
int main(){
int value = 3;
std::queue<type_ptr> queue;
auto ptr{std::make_shared<type>(value)};
queue.push(ptr);
}
The compiler outputs the following errors:
src/test.cc: In function ‘int main()’:
src/test.cc:15:17: error: no matching function for call to ‘std::queue<std::shared_ptr<type> >::push(std::initializer_list<std::shared_ptr<type> >&)’
src/test.cc:15:17: note: candidates are:
In file included from /usr/include/c++/4.7/queue:65:0,
from src/test.cc:2:
/usr/include/c++/4.7/bits/stl_queue.h:211:7: note: void std::queue<_Tp, _Sequence>::push(const value_type&) [with _Tp = std::shared_ptr<type>; _Sequence = std::deque<std::shared_ptr<type>, std::allocator<std::shared_ptr<type> > >; std::queue<_Tp, _Sequence>::value_type = std::shared_ptr<type>]
/usr/include/c++/4.7/bits/stl_queue.h:211:7: note: no known conversion for argument 1 from ‘std::initializer_list<std::shared_ptr<type> >’ to ‘const value_type& {aka const std::shared_ptr<type>&}’
/usr/include/c++/4.7/bits/stl_queue.h:216:7: note: void std::queue<_Tp, _Sequence>::push(std::queue<_Tp, _Sequence>::value_type&&) [with _Tp = std::shared_ptr<type>; _Sequence = std::deque<std::shared_ptr<type>, std::allocator<std::shared_ptr<type> > >; std::queue<_Tp, _Sequence>::value_type = std::shared_ptr<type>]
/usr/include/c++/4.7/bits/stl_queue.h:216:7: note: no known conversion for argument 1 from ‘std::initializer_list<std::shared_ptr<type> >’ to ‘std::queue<std::shared_ptr<type> >::value_type&& {aka std::shared_ptr<type>&&}’
Indicating that the auto type is expanded to an initializer list instead of std::shared_ptr<type>; in fact replacing {...} with = ... makes the code compile as auto expands to the correct type.
I'm a bit suprised that this seemingly obvious use case fails to achieve the expected result. Especially as I recall the new bracket initialization syntax to be touted as the end-all, be-all solution to initializing problems.
So my question is: Was this intended in the standard? Or is it an oversight or even a gcc bug? Or am I just thinking about it wrong?
As Xeo says in his comment, this is standard behavior. 7.1.6.4 auto specifier [dcl.spec.auto] para 6 specifies:
Once the type of a declarator-id has been determined according to 8.3, the type of the declared variable using the declarator-id is determined from the type of its initializer using the rules for template argument deduction. Let T be the type that has been determined for a variable identifier d. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list (8.5.4), with std::initializer_list<U>. The type deduced for the variable d is then the deduced A determined using the rules of template argument deduction from a function call (14.8.2.1), where P is a function template parameter type and the initializer for d is the corresponding argument. If
the deduction fails, the declaration is ill-formed.
It is also widely despised - there's a proposal under review by the committee to change the behavior for C++14. C++14's support for generalized lambda capture exacerbates the problem.
Update: In Urbana (See CWG Motion 16 in N4251 WG21 2014-11 Urbana Minutes) the committee applied N3922 New Rules for auto deduction from braced-init-list to the C++17 Working Paper. They decided to fix the special case that allows auto to deduce an initializer_list by adding another special case. auto works the same way for copy-list-initialization, but for direct-list-initialization from a braced-init-list with a single element auto deduces from that element directly. direct-list-initialization from a multiple-element braced-init-list is now ill-formed.
That means that given
auto x = {42};
x has type std::initializer_list<int>, but in
auto x{42};
x is an int.

Setting a std::function variable to refer to the std::sin function

I've got a question about how to properly use the new C++11 std::function variable. I've seen several examples from searching the Internet, but they don't seem to cover the usage case I'm considering. Take this minimum example, where the function fdiff is an implementation of the finite forward differencing algorithm defined in numerical.hxx (which isn't the problem, I just wanted to give a contextual reason why I'd want to take an arbitrary function and pass it around).
#include <functional>
#include <iostream>
#include <cmath>
#include "numerical.hxx"
int main()
{
double start = 0.785398163;
double step = 0.1;
int order = 2;
std::function<double(double)> f_sin = std::sin;
std::cout << fdiff(start, step, order, f_sin) << std::endl;
return 0;
}
Attempting to compile the above program gives me the error (in clang++)
test.cpp:11:32: error: no viable conversion from '<overloaded function type>' to
'std::function<double (double)>'
std::function<double(double)> f_sin = std::sin;
^ ~~~~~~~~
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.1/../../../../include/c++/4.7.1/functional:2048:7: note:
candidate constructor not viable: no overload of 'sin' matching
'nullptr_t' for 1st argument
function(nullptr_t) noexcept
^
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.1/../../../../include/c++/4.7.1/functional:2059:7: note:
candidate constructor not viable: no overload of 'sin' matching 'const
std::function<double (double)> &' for 1st argument
function(const function& __x);
^
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.1/../../../../include/c++/4.7.1/functional:2068:7: note:
candidate constructor not viable: no overload of 'sin' matching
'std::function<double (double)> &&' for 1st argument
function(function&& __x) : _Function_base()
^
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.1/../../../../include/c++/4.7.1/functional:2092:2: note:
candidate template ignored: couldn't infer template argument '_Functor'
function(_Functor __f,
^
1 error generated.
or from g++
test.cpp: In function ‘int main()’:
test.cpp:11:45: error: conversion from ‘<unresolved overloaded function type>’ to non-scalar type ‘std::function<double(double)>’ requested
As I understand the problem, it's because std::sin is implemented as a template class in the standard library, but I can't seem to figure out what I need to do to give enough of a specialization to get a function reference. I've also tried various things like using the new auto keyword, using &std::sin to get a pointer, etc., but they all give me the same type of error.
std::sin is an overloaded function: you must disambiguate which std::sin overload you mean:
std::function<double(double)> f_sin = (double(*)(double))&std::sin;
There are some cases where the compiler can disambiguate overloaded functions (e.g., if f_sin was of type double(*)(double), the cast would not be required). However, this is not one of those cases.
With lambda you will be always on safe side:
std::function<double(double)> f_sin = [](double arg) -> double { return std::sin(arg); };
Actually you can do better, if you can change fdiff or it is already accepting template parameter - not just std::function<double(double)>:
auto f_sin = [](double arg) -> double { return std::sin(arg); };
std::cout << fdiff(start, step, order, f_sin) << std::endl;
[UPDATE] This answer is new version, previous advice to use function template specialization was incorrect, since std::sin is not function template but set of overloaded functions.

What's wrong with copy-construction of a shared_ptr from an implicit cast?

Consider this minimal example:
#include <memory>
struct B {
typedef std::shared_ptr<B> Ptr;
};
struct A {
operator B::Ptr() { // type conversion operator <----+
return std::make_shared<B>(); // |
} // |
}; // |
// |
int main() { // |
A* a = new A; // |
B::Ptr{*a}; // copy construction from a's implicit cast to B::Ptr ----+
}
This innocent copy construction of a shared_ptr<B> fails horribly on g++ 4.6.3 x86_64-linux-gnu but appears to work for g++ 4.5 (note that the newer version breaks, while the older works!). From what I can tell from the error (see below) g++ 4.6 seems to pass A by value, instead of by (r or l) reference.
So, the question is, which is correct and which is broken? Is this behaviour supposed to fail? If so, why?
As far as I understand conversion rules, the implicit cast to B::Ptr should be attempted at this point, right?
Note: I reduced this example to the bare technical problem, and this code doesn't make sense for any production-system as it stands.
Here is the precise error:
shp.cpp: In function ‘int main()’:
shp.cpp:17:12: error: no matching function for call to ‘std::shared_ptr<B>::shared_ptr(<brace-enclosed initializer list>)’
shp.cpp:17:12: note: candidates are:
/usr/include/c++/4.6/bits/shared_ptr.h:315:2: note: template<class _Alloc, class ... _Args> std::shared_ptr::shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...)
/usr/include/c++/4.6/bits/shared_ptr.h:266:17: note: constexpr std::shared_ptr<_Tp>::shared_ptr(std::nullptr_t) [with _Tp = B, std::nullptr_t = std::nullptr_t]
/usr/include/c++/4.6/bits/shared_ptr.h:266:17: note: no known conversion for argument 1 from ‘A’ to ‘std::nullptr_t’
/usr/include/c++/4.6/bits/shared_ptr.h:258:2: note: template<class _Tp1, class _Del> std::shared_ptr::shared_ptr(std::unique_ptr<_Up, _Ep>&&)
/usr/include/c++/4.6/bits/shared_ptr.h:253:2: note: template<class _Tp1> std::shared_ptr::shared_ptr(std::auto_ptr<_Tp1>&&)
/usr/include/c++/4.6/bits/shared_ptr.h:248:11: note: template<class _Tp1> std::shared_ptr::shared_ptr(const std::weak_ptr<_Tp1>&)
/usr/include/c++/4.6/bits/shared_ptr.h:236:2: note: template<class _Tp1, class> std::shared_ptr::shared_ptr(std::shared_ptr<_Tp1>&&)
/usr/include/c++/4.6/bits/shared_ptr.h:226:7: note: std::shared_ptr<_Tp>::shared_ptr(std::shared_ptr<_Tp>&&) [with _Tp = B, std::shared_ptr<_Tp> = std::shared_ptr<B>]
/usr/include/c++/4.6/bits/shared_ptr.h:226:7: note: no known conversion for argument 1 from ‘A’ to ‘std::shared_ptr<B>&&’
/usr/include/c++/4.6/bits/shared_ptr.h:218:2: note: template<class _Tp1, class> std::shared_ptr::shared_ptr(const std::shared_ptr<_Tp1>&)
/usr/include/c++/4.6/bits/shared_ptr.h:206:2: note: template<class _Tp1> std::shared_ptr::shared_ptr(const std::shared_ptr<_Tp1>&, _Tp*)
/usr/include/c++/4.6/bits/shared_ptr.h:184:2: note: template<class _Deleter, class _Alloc> std::shared_ptr::shared_ptr(std::nullptr_t, _Deleter, _Alloc)
/usr/include/c++/4.6/bits/shared_ptr.h:165:2: note: template<class _Tp1, class _Deleter, class _Alloc> std::shared_ptr::shared_ptr(_Tp1*, _Deleter, _Alloc)
/usr/include/c++/4.6/bits/shared_ptr.h:146:2: note: template<class _Deleter> std::shared_ptr::shared_ptr(std::nullptr_t, _Deleter)
/usr/include/c++/4.6/bits/shared_ptr.h:129:2: note: template<class _Tp1, class _Deleter> std::shared_ptr::shared_ptr(_Tp1*, _Deleter)
/usr/include/c++/4.6/bits/shared_ptr.h:112:11: note: template<class _Tp1> std::shared_ptr::shared_ptr(_Tp1*)
/usr/include/c++/4.6/bits/shared_ptr.h:103:7: note: std::shared_ptr<_Tp>::shared_ptr(const std::shared_ptr<_Tp>&) [with _Tp = B, std::shared_ptr<_Tp> = std::shared_ptr<B>]
/usr/include/c++/4.6/bits/shared_ptr.h:103:7: note: no known conversion for argument 1 from ‘A’ to ‘const std::shared_ptr<B>&’
/usr/include/c++/4.6/bits/shared_ptr.h:100:17: note: constexpr std::shared_ptr<_Tp>::shared_ptr() [with _Tp = B]
/usr/include/c++/4.6/bits/shared_ptr.h:100:17: note: candidate expects 0 arguments, 1 provided
The code is incorrect under the current version of the standard (I'm looking at post-standard draft n3376).
The rules for list-initialization specify:
13.3.1.7 Initialization by list-initialization [over.match.list]
1 - When objects of non-aggregate class type T are list-initialized [...]:
If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.
However, when overload resolution is applied to the copy constructor of B::Ptr taking the single parameter const std::shared_ptr<B> &, the argument list is (*a), consisting of a single element of type lvalue A; overload resolution is not permitted to consider the conversion function A::operator B::Ptr:
13.3.3.1 Implicit conversion sequences [over.best.ics]
4 - However, when considering the argument of a constructor or user-defined conversion function that is a candidate [...] by 13.3.1.7 [...] when the initializer list has exactly one element and a conversion to some class X or reference to (possibly cv-qualified) X is considered for the first parameter of a constructor of X [...], only standard conversion sequences and ellipsis conversion sequences are considered.
So g++-4.6 is correct to reject this code; g++-4.7.2 unfortunately accepts it, which is incorrect.
The correct way to write this would be to use direct-initialization (B::Ptr(*a)) or a static_cast<B::Ptr>.
The restriction on the allowable conversions can be traced to paper n2672, although in that paper the paragraph 13.3.3.1p4 only applies to the argument of a user-defined conversion function. The additional restriction on constructors was added in defect 978:
978. Incorrect specification for copy initialization
13.3.3.1 [over.best.ics] paragraph 4 says,[...]
This is not quite right, as this applies to constructor arguments, not just arguments of user-defined conversion functions.
The current wording of 13.3.3.1p4 can be traced to the seminal defect 84, which introduced the "common-law rule that only a single user-defined conversion will be called to do an implicit conversion".
I'm a bit uneasy about this answer; I've asked Is it possible to invoke a user-defined conversion function via list-initialization? to see if anyone can clarify the intent of the standard here.