(non)ambiguous static overload within templated class - c++

My class template NodeMaker has 3 static member function templates called create_node which are distinguished by their argument(s) using C++20 concepts. When calling NodeMaker<>::create_node(x) from main() everything works as I intended, but when calling create_node from another member function, GCC claims ambiguous overload, and I fail to understand why.
#include <utility>
#include <type_traits>
template<class F> concept NodeFunctionType = std::invocable<std::remove_reference_t<F>, int>;
template<class T> concept ExtracterType = requires { typename T::I_am_an_extracter; };
template<class T = int>
struct NodeMaker {
template<class... Args>
static constexpr auto create_node(Args&&... args) { return new T(std::forward<Args>(args)...); }
template<NodeFunctionType DataMaker> requires (!ExtracterType<DataMaker>)
static constexpr auto create_node(DataMaker&& data_maker) { return create_node(); } // line 13
template<ExtracterType DataMaker> requires (!NodeFunctionType<DataMaker>)
static constexpr auto create_node(DataMaker&& data_maker) { return create_node(); } // line 16
void do_something() {
const auto target = create_node(0); //this does not work in gcc
}
};
int main(const int argc, const char** argv) {
const auto target = NodeMaker<>::create_node(0); //but this works
}
GCC errors out when compiling:
<source>: In member function 'void NodeMaker<T>::do_something()':
<source>:19:36: error: call of overloaded 'create_node(int)' is ambiguous
19 | const auto target = create_node(0);
<source>:10:25: note: candidate: 'static constexpr auto NodeMaker<T>::create_node(Args&& ...) [with Args = {int}]'
10 | static constexpr auto create_node(Args&&... args) { return new T(std::forward<Args>(args)...); }
<source>:13:25: note: candidate: 'static constexpr auto NodeMaker<T>::create_node(DataMaker&&) [with DataMaker = int]'
13 | static constexpr auto create_node(DataMaker&& data_maker) { return create_node(); }
<source>:16:25: note: candidate: 'static constexpr auto NodeMaker<T>::create_node(DataMaker&&) [with DataMaker = int]'
16 | static constexpr auto create_node(DataMaker&& data_maker) { return create_node(); }
But,
13 | static constexpr auto create_node(DataMaker&& data_maker)
cannot possibly match because int does not satisfy NodeFunctionType and
16 | static constexpr auto create_node(DataMaker&& data_maker)
cannot possibly match because int does not satisfy ExtracterType. Further, under no circumstances can both function templates match since their constraints are obviously mutually exclusive.
So my question would be: why can't GCC disambiguate the call from a member function (but can do it when called from outside the class)?
PS: see godbolt.

This seems to be a gcc bug. Here is the submitted bug report.
It seems that gcc requires the call to the static method create_node to be explicitly qualified with NodeMaker<>:: even from inside the non-static member function do_something.
You can confirm that this is the case by adding NodeMaker<>:: and you'll see that it then works in gcc.
void do_something() {
//----------------------vvvvvvvvvvvvv--------------->added this qualification
const auto target = NodeMaker<>::create_node(0); //works now in gcc
}
Demo
Note also that if you use NodeMaker<T>::create_node(0); from inside do_something then the issue will reappear.

Related

Recursive noexcept specification inside class/struct

Follows up on the question Recursive noexcept specification. If I declare function f in scope of class or struct, it should be visible everywhere inside class/struct scope. Also in noexcept specifier (it should not matter if it is recursive call). MSVC v19.28 and Clang 12.0.1 accept this and compile this code, but GCC 11.2 does not? Why? Is it bug in GCC compiler or in MSVC and Clang?
struct S {
template<typename T>
static auto f(T && t) noexcept {
return true;
}
template<typename T, typename... Ts>
static auto f(T && t, Ts && ... ts) noexcept(noexcept(f(ts...))) {
return f(ts...);
}
};
int main() {
S::f(true, 0, 5u);
}
GCC error message:
In instantiation of 'static auto S::f(T&&, Ts&& ...) [with T = bool; Ts
= {int, unsigned int}]':
error: no matching function for call to 'S::f(int&, unsigned int&)'
static auto f(T && t, Ts && ... ts) noexcept(noexcept(f(ts...))) {
~^~~~~~~
note: candidate: 'template<class T> static auto S::f(T&&)'
static auto f(T && t) noexcept {
^
note: template argument deduction/substitution failed:
note: candidate expects 1 argument, 2 provided
static auto f(T && t, Ts && ... ts) noexcept(noexcept(f(ts...))) {
~^~~~~~~
I believe this is a GCC bug introduced in GCC 8. As you can see here, when compiled with GCC 7.5, everything compiles successfully.
Why should this compile successfully?
Because the noexcept-specifier of a specialization of a function template is instantiated only when needed.

Incomplete type `std::variant<...>` used in nested name specifier

I wrote the following code into a file named main.cpp.
It involves the curiously recurring template pattern (CRTP) with the standard type std::variant.
#include <string>
#include <variant>
#include <vector>
template<typename T>
struct either {
std::vector<T> arg;
};
template<typename T>
struct maybe_either: std::variant<T, either<maybe_either<T>>> {
template<typename U>
maybe_either(U&& v):
std::variant<T, either<maybe_either<T>>>(std::forward<U>(v)) {
}
};
struct var {
std::string name;
};
int main(int, char**) {
auto expression = maybe_either<var>(either<maybe_either<var>>{});
std::visit([&](auto&& v) {
using T = std::decay_t<decltype (v)>;
if constexpr (std::is_same_v<T, var>) {
// ...
} else if constexpr (std::is_same_v<T, either<maybe_either<var>>>) {
// ...
}
}, expression);
return 0;
}
When compiling it with the following command line, I get the error message below:
$ g++ -c -std=c++17 main.cpp
In file included from main.cpp:2:0:
/usr/include/c++/7/variant: In instantiation of ‘constexpr const size_t std::variant_size_v<maybe_either<var> >’:
/usr/include/c++/7/variant:702:10: required from ‘struct std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>’
/usr/include/c++/7/variant:1255:23: required from ‘constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = main(int, char**)::<lambda(auto:1&&)>; _Variants = {maybe_either<var>&}]’
main.cpp:32:18: required from here
/usr/include/c++/7/variant:97:29: error: incomplete type ‘std::variant_size<maybe_either<var> >’ used in nested name specifier
inline constexpr size_t variant_size_v = variant_size<_Variant>::value;
^~~~~~~~~~~~~~
/usr/include/c++/7/variant: In instantiation of ‘constexpr const auto std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>::_S_vtable’:
/usr/include/c++/7/variant:711:29: required from ‘struct std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>’
/usr/include/c++/7/variant:1255:23: required from ‘constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = main(int, char**)::<lambda(auto:1&&)>; _Variants = {maybe_either<var>&}]’
main.cpp:32:18: required from here
/usr/include/c++/7/variant:711:49: error: ‘_S_apply’ was not declared in this scope
static constexpr auto _S_vtable = _S_apply();
~~~~~~~~^~
My class maybe_either derived from std::variant<...> can be used normally in other contexts, but when I call std::visit(...) on it, it fails to compile. What is wrong?
This is basically LWG3052 which I'm trying to address in P2162.
maybe_either<T> isn't a specialization of std::variant - it inherits from one. And std::visit is currently underspecified. It's not at all clear what kinds of "variants" are allowed to be visited.
libstdc++ implements the original suggested direction in that library issue, which is only specializations of std::variant (of which you are not). libc++, on the other hand, allows types that inherit from std::variant to be visited, so it accepts your example.
The intent is that the example as-is will become well-formed eventually. But until then, you'll have to ensure that the visit you do is directly on a std::variant. You can do so by adding your own member or non-member visit that does this cast for you, so the callers don't have to do it themselves.
For example, this:
template<typename T>
struct maybe_either: std::variant<T, either<maybe_either<T>>> {
using base = typename maybe_either::variant;
template<typename U>
maybe_either(U&& v):
std::variant<T, either<maybe_either<T>>>(std::forward<U>(v)) {
}
template <typename F>
decltype(auto) visit(F&& f) & {
return std::visit(std::forward<F>(f), static_cast<base&>(*this));
}
};
allows this to work:
int main(int, char**) {
auto expression = maybe_either<var>(either<maybe_either<var>>{});
expression.visit([&](auto&& v) {
using T = std::decay_t<decltype (v)>;
if constexpr (std::is_same_v<T, var>) {
// ...
} else if constexpr (std::is_same_v<T, either<maybe_either<var>>>) {
// ...
}
});
return 0;
}

hana simple fold "call to non-constexpr function"

I simplified my code producing an error, and found that even this simple counting function gave me an error (see below):
#include <boost/hana/tuple.hpp>
#include <boost/hana/fold.hpp>
#include <boost/hana/plus.hpp>
#include <boost/hana/integral_constant.hpp>
int main() {
using namespace boost;
constexpr auto inc = [](auto n, auto el) { return hana::int_c<n> + hana::int_c<1>; };
constexpr auto count = hana::fold(hana::make_tuple(hana::int_c<3>),
hana::int_<0>{},
inc
);
return 0;
}
Error (ommitted some that seemed irrelevant):
/usr/local/include/boost/hana/detail/variadic/foldl1.hpp:202:57: error: ‘static constexpr decltype(auto) boost::hana::detail::variadic::foldl1_impl<2u>::apply(F&&, X1&&, X2&&) [with F = const main()::<lambda(auto:1, auto:2)>&; X1 = boost::hana::integral_constant<int, 0>; X2 = boost::hana::integral_constant<int, 3>]’ called in a constant expression
return foldl1_impl<sizeof...(xn) + 1>::apply(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
static_cast<F&&>(f), static_cast<X1&&>(x1), static_cast<Xn&&>(xn)...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
);
~
In file included from /usr/local/include/boost/hana/fold_left.hpp:18:0,
from /usr/local/include/boost/hana/concept/foldable.hpp:19,
from /usr/local/include/boost/hana/core/to.hpp:16,
from /usr/local/include/boost/hana/bool.hpp:17,
from /usr/local/include/boost/hana/tuple.hpp:16,
from ...:
/usr/local/include/boost/hana/detail/variadic/foldl1.hpp:31:41: note: ‘static constexpr decltype(auto) boost::hana::detail::variadic::foldl1_impl<2u>::apply(F&&, X1&&, X2&&) [with F = const main()::<lambda(auto:1, auto:2)>&; X1 = boost::hana::integral_constant<int, 0>; X2 = boost::hana::integral_constant<int, 3>]’ is not usable as a constexpr function because:
static constexpr decltype(auto) apply(F&& f, X1&& x1, X2&& x2) {
^~~~~
/usr/local/include/boost/hana/detail/variadic/foldl1.hpp:32:39: error: call to non-constexpr function ‘main()::<lambda(auto:1, auto:2)> [with auto:1 = boost::hana::integral_constant<int, 0>; auto:2 = boost::hana::integral_constant<int, 3>]’
return static_cast<F&&>(f)(static_cast<X1&&>(x1),
~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
static_cast<X2&&>(x2));
I used c++17 with g++ 6.3. It looks like it says that the lambda isn't seen as a constexpr, but it looks to me like it uses only constant values. Can anyone suggest to me how I can make this code work? (It's purpose is to count the number of elements in the tuple passed to fold)
If your compiler doesn't support constexpr lambdas, you have to write out the closure types yourself (at namespace or class scope, not at function scope since a local class cannot have member templates):
constexpr struct inc_t {
template<class N, class T>
constexpr auto operator()(N n, T) const { return hana::int_c<n> + hana::int_c<1>; }
} inc;
Otherwise, you can still use hana but the result of computations will not be available to the type system.

Convert constexpr struct to a runtime one

I'm trying to use a template class (here Foo), with a basic type like:
hana::tuple<hana::pair<hana::type<int>, Runtime>> with Runtime a class which obiviously can't be constepxr.
But the type can be construct in several way that's why I use:
hana::tuple<hana::pair<hana::type<int>, hana::type<Runtime>>> to do the work at compile time.
So the question is basically how convert from the first tuple type to the second one. I wonder if there is something in hana that could help me. Or even better, some tips on that kind of "conversion".
namespace hana = boost::hana;
using namespace hana::literals;
struct Runtime { std::vector<int> data; };
template < typename T >
struct Foo {
T data;
};
constexpr decltype(auto) convertMap(auto storageMap) {
return hana::make_type(hana::transform(
storageMap,
[] (auto pair) {
return hana::make_pair(
hana::first(pair),
typename decltype(hana::typeid_(hana::second(pair)))::type {});
}));
}
int main() {
constexpr auto map = hana::make_tuple(
hana::make_pair(hana::type_c<int>, hana::type_c<Runtime>)
);
constexpr auto result = convertMap(map);
static_assert(result ==
hana::type_c<hana::tuple<hana::pair<hana::type<int>, Runtime>>>);
Foo<typename decltype(result)::type> test;
}
As you can see I tried some c++1z convertMap with hana::transform and lambdas, but the second tuple can't be constexpr, so I can't pass it to hana::make_type hoping to get a hana::type_c.
test.cpp: In function ‘int main()’:
test.cpp:70:41: error: ‘constexpr decltype(auto) convertMap(auto:27) [with auto:27 = boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, boost::hana::type_impl<Runtime>::_> >]’ called in a constant expression
constexpr auto result = convertMap(map);
^
test.cpp:53:27: note: ‘constexpr decltype(auto) convertMap(auto:27) [with auto:27 = boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, boost::hana::type_impl<Runtime>::_> >]’ is not usable as a constexpr function because:
constexpr decltype(auto) convertMap(auto storageMap) {
^~~~~~~~~~
test.cpp:53:27: error: temporary of non-literal type ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ in a constant expression
In file included from /usr/include/boost/hana/detail/struct_macros.hpp:29:0,
from /usr/include/boost/hana/adapt_adt.hpp:15,
from lib/hana/include/boost/hana.hpp:59,
from test.cpp:1:
/usr/include/boost/hana/tuple.hpp:68:12: note: ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ is not literal because:
struct tuple
^~~~~
/usr/include/boost/hana/tuple.hpp:68:12: note: ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ has a non-trivial destructor
All you care about is a type - you can therefore hide the computation of the type in a non-constexpr implementation function, and afterwards call it with decltype(...){} to "force constexpr":
template <typename T>
decltype(auto) convertMapImpl(T storageMap)
{
return hana::make_type(hana::transform(storageMap, [](auto pair) {
return hana::make_pair(hana::first(pair),
typename decltype(hana::typeid_(hana::second(pair)))::type{});
}));
}
template <typename T>
constexpr decltype(auto) convertMap(T storageMap)
{
return decltype(convertMapImpl(storageMap)){};
}
live wandbox example
Also note that using auto in a function signature is a gcc extension - you should use a template parameter instead to be standard-compliant.

Simple constexpr function failed to compile with GCC (clang is OK)

The following code does not compile with GCC 5.2 (C++14). It does compile with clang 3.6 (C++14). (original code can be found here)
#include <cstddef>
#include <algorithm>
#include <type_traits>
#include <utility>
template <typename T>
class aggregate_wrapper;
template <typename T, std::size_t n>
class aggregate_wrapper<T[n]> {
public:
using array = T[n];
template <typename... Ts, typename = decltype(array{std::declval<Ts>()...})>
aggregate_wrapper(Ts&&... xs)
: arr_{std::forward<Ts>(xs)...} {
// nop
}
aggregate_wrapper(const array& arr) {
std::copy(arr, arr + n, arr_);
}
aggregate_wrapper(array&& arr) {
std::move(arr, arr + n, arr_);
}
operator T* () {
return arr_;
}
operator const T* () const {
return arr_;
}
constexpr std::size_t size() const {
return n;
}
private:
array arr_;
};
int main() {
aggregate_wrapper<int[3]> arr;
static_assert(arr.size() == 3, "");
}
The error message produced is
main.cpp: In function 'int main()':
main.cpp:44:3: error: non-constant condition for static assertion
static_assert(arr.size() == 3, "");
^
main.cpp:44:25: error: call to non-constexpr function 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]'
static_assert(arr.size() == 3, "");
^
main.cpp:34:25: note: 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]' is not usable as a constexpr function because:
constexpr std::size_t size() const {
^
main.cpp:34:25: error: enclosing class of constexpr non-static member function 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]' is not a literal type
main.cpp:10:7: note: 'aggregate_wrapper<int [3]>' is not literal because:
class aggregate_wrapper<T[n]> {
^
main.cpp:10:7: note: 'aggregate_wrapper<int [3]>' is not an aggregate, does not have a trivial default constructor, and has no constexpr constructor that is not a copy or move constructor
Any ideas? Should the code compile according to the standard?
Or you could just make your existing variadic constructor serve as your constexpr constructor to perform the default construction:
template <typename... Ts, typename = decltype(array{std::declval<Ts>()...})>
constexpr // <---- ADD THIS
aggregate_wrapper(Ts&&... xs)
: arr_{std::forward<Ts>(xs)...} {
// nop
}
In order for g++ to get it compiled you will need to add a default constructor:
aggregate_wrapper() = default;
please see it in action at: http://coliru.stacked-crooked.com/a/df1ac057960bebc7
I have the feeling that clang under the hood added it, but I am not 100% sure ...
GCC is wrong. Its diagnostic, in part, says:
main.cpp:34:25: note: '<...>' is not usable as a constexpr function because:
main.cpp:34:25: error: enclosing class of constexpr non-static member function '<...>' is not a literal type
... but there is no such rule. See [dcl.constexpr]/3 for the list of constraints that apply here.
You can work around the bogus GCC diagnostic by adding a dummy constexpr constructor (it's fine for that constructor to be private and/or deleted if you don't want any of your real constructors to be constexpr) or by making size be static.