This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug c++/71117] New: [6.1 regression] Overeager application of conversion to function pointer during overload resolution of call to function object
- From: "eric.niebler at gmail dot com" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: Sat, 14 May 2016 19:38:13 +0000
- Subject: [Bug c++/71117] New: [6.1 regression] Overeager application of conversion to function pointer during overload resolution of call to function object
- Auto-submitted: auto-generated
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71117
Bug ID: 71117
Summary: [6.1 regression] Overeager application of conversion
to function pointer during overload resolution of call
to function object
Product: gcc
Version: 6.1.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: eric.niebler at gmail dot com
Target Milestone: ---
Casey Carter recently posted this to the committee core- mailing list:
<<<<<<<<<<< BEGIN
It is a commonplace library technique to compose function objects, which are
often empty, by inheritance to take advantage of the Empty Base Optimization.
E.g., this program patterned on usage in range-v3 turns a function that accepts
objects into a function that accepts iterators that denote objects:
template <class T> T&& declval() noexcept;
template <class, class>
constexpr bool is_same = false;
template <class T>
constexpr bool is_same<T, T> = true;
template <class F>
struct indirected : F {
indirected(F f) : F(f) {}
template <class I>
auto operator()(I i) -> decltype(declval<F&>()(*i)) {
return static_cast<F&>(*this)(*i);
}
};
int main() {
auto f = [](auto rng) {
static_assert(is_same<decltype(rng), int>, "");
return 42;
};
indirected<decltype(f)> i(f);
static_assert(is_same<decltype(i(declval<int*>())), int>, "");
}
Unfortunately, when the adapted function object is an underconstrained
captureless generic lambda - as is the case in the example - composition by
inheritance is extremely fragile. Since the lambda is captureless, its closure
type has a member conversion operator that converts to a function pointer type
with deduced return type. An attempt to call the derived type's call operator
results in overload resolution instantiating the conversion operator's
declaration necessitating return type deduction from the base object's call
operator. When the base object's call operator is ill-formed for the particular
argument types - again as is the case in the example - the program is
ill-formed.
GCC 6 and trunk tell me this program is ill-formed:
casey@Semiregular:~/bugs$ ~/gcc/bin/g++ -std=c++14 repro.cpp
repro.cpp: In instantiation of âmain()::<lambda(auto:1)> [with auto:1 = int*]â:
repro.cpp:17:25: required by substitution of âtemplate<class auto:1>
main()::<lambda(auto:1)>::operator decltype
(((main()::<lambda(auto:1)>)0u).operator()(static_cast<auto:1>(<anonymous>)))
(*)(auto:1)() const [with auto:1 = int*]â
repro.cpp:22:53: required from here
repro.cpp:18:9: error: static assertion failed
static_assert(is_same<decltype(rng), int>, "");
^~~~~~~~~~~~~
older versions of GCC and all versions of Clang I've tried compile the program
without diagnostics. Since hiding of conversion operators is based on the
target types, and determining the target type of the problematic conversion
operator results in the aforementioned ill-formed return type deduction, there
seems to be no way to hide the problematic conversion operator.
>>>>>>>>>>>>>>>>>>>>>>>> END
To which Richard Smith replied:
<<<<<<<<<<<<<<<<<<<<<<<< BEGIN
template<typename T> using id = T;
struct F {
template<typename T> operator id<T(*)(T)>();
} f;
int n = f(0);
GCC accepts this and calls the conversion function template with T=int. Clang,
EDG, MSVC reject.
Per [over.call.object]/2, I think GCC is wrong per the current language
wording. A conversion function template does not qualify as a "non-explicit
conversion function declared in [F or base class thereof]" (because it is not a
conversion function), and nor does a specialization of one (because it is not
itself declared in F).
I also don't see any way the current wording would take us to
[temp.deduct.conv] for this case, nor how that wording would apply (since we
don't have a function type that's required as the result of the conversion).
Perhaps GCC is following the rules of [temp.deduct.call] in this case, treating
the (pointee type of the) result type of the conversion function as if it were
the function type of the callee?
In the abstract, GCC's approach to this situation seems superior -- it's able
to use a conversion to function pointer in many cases where other compilers
can't -- but I'm a little hesitant to suggest we adopt that approach since it
breaks examples like yours.
>>>>>>>>>>>>>>>>>>>>>>>>> END