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.
Related
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.
struct Outer
{
explicit constexpr Outer(int ii) : n(ii) {}
explicit constexpr Outer(double dd) : n(dd) {}
explicit constexpr Outer(double*) : Outer(3.1415926) {}
explicit constexpr Outer(int*): Outer(42) {}
template <typename R>
static
constexpr
Outer meow(R* r = nullptr) { return Outer(r); }
int as_i() const { return n.i; }
double as_d() const { return n.d; }
union Inner
{
int i;
double d;
constexpr Inner(int ii) : i(ii) {}
constexpr Inner(double dd) : d(dd) {}
~Inner () {}; // non-trivial destructor
};
Inner n;
};
//#include <type_traits>
//static_assert(std::is_literal_type_v<Outer>); // failed by both GCC and clang
int main(int argc, char**)
{
auto const o = Outer::meow(&argc);
return o.as_i();
}
The above code is compiled by GCC 12.1 but rejected by clang 14.0.0
https://godbolt.org/z/o6Tvrrdsv
clang (correctly, in my opinion) complains that
<source>:12:11: error: constexpr function's return type 'Outer' is not a literal type
Outer meow(R* r = nullptr) { return Outer(r); }
^
<source>:28:11: note: 'Outer' is not literal because it has data member 'n' of non-literal type 'Outer::Inner'
Inner n;
^
<source>:36:24: error: no matching function for call to 'meow'
auto const o = Outer::meow(&argc);
^~~~~~~~~~~
<source>:12:11: note: candidate template ignored: substitution failure [with R = int]
Outer meow(R* r = nullptr) { return Outer(r); }
^
Even though o is merely const, not constexpr, shouldn't this be an error? meow is trying to do something which shouoldn't be allowed.
(If I make o into constexpr, then GCC complains that Outer has a non-trivial destructor.)
Yes, the return type of a constexpr function must be a literal type and Outer is not. If a function definition doesn't satisfy the constexpr requirements, the program would be ill-formed.
However, you have a function template here. constexpr on a function template is allowed as long as there is at least one specialization which satisfies the requirements of a constexpr function. However, if this requirement is not fulfilled, the program isn't ill-formed. It is ill-formed, no diagnostic required (IFNDR).
There is no specialization here satisfying the requirements because you always have the Outer return type and so the program is IFNDR.
Both compilers are correct. They do not need to diagnose that the program is ill-formed.
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;
}
The following code issues a warning when compiling with gcc, but only with version <= 9.3
#include <array>
#include <iostream>
template <std::size_t size>
struct A {
using atype = std::array<double, size>;
template <std::size_t index = 0>
static constexpr void fill_array(atype& res)
{
std::get<index>(res) = 1;
if constexpr (index < (size - 1))
fill_array<index + 1>(res);
}
static constexpr atype get_array()
{
atype res;
fill_array(res);
return res;
}
};
int main()
{
auto x = A<3>::get_array();
for (const auto e: x)
std::cout << e << ' ';
}
Test it on godbolt. I am compiling with -Wall -pedantic -std=c++17 -O3. The issued warning is
In file included from <source>:1:
<source>: In instantiation of 'static constexpr A<size>::atype A<size>::get_array() [with long unsigned int size = 3; A<size>::atype = std::array<double, 3>]':
<source>:26:30: required from here
/opt/compiler-explorer/gcc-9.3.0/include/c++/9.3.0/array:94:12: note: 'using atype = struct std::array<double, 3>' {aka 'struct std::array<double, 3>'} has no user-provided default constructor
94 | struct array
| ^~~~~
/opt/compiler-explorer/gcc-9.3.0/include/c++/9.3.0/array:110:56: note: and the implicitly-defined constructor does not initialize 'double std::array<double, 3>::_M_elems [3]'
110 | typename _AT_Type::_Type _M_elems;
| ^~~~~~~~
Compiler returned: 0
While the std::array of double is uninitialized, actually the recursive template routine initializes all elements of res. So, the warning is not "real". In principle, I could "hack" the code by calling fill_array<1>, thereby skipping the initialization of the 0 component. But code for template functions is generated only when instantiated with given template parameters, so again, in the above code the compiler never skips to generate fill_array<0>.
Stragely, this warning only appears up to version 9.3 of gcc. Also, clang does not issue the warning.
Even more strangely, when the functions are not embedded into a class, the warning disappear. With the following code:
#include <array>
#include <iostream>
constexpr std::size_t size = 3;
using atype = std::array<double, size>;
template <std::size_t index = 0>
void fill_array(atype& res)
{
std::get<index>(res) = 1;
if constexpr (index < (size - 1))
fill_array<index + 1>(res);
}
atype get_array()
{
atype res;
fill_array(res);
return res;
}
int main()
{
auto x = get_array();
for (const auto e: x)
std::cout << e << ' ';
}
no warning are displayed, despite being apparently identical to the first code. Test it here.
Is there a reason for the different compiler behavior between the two codes?
Can I suppress the warning only for this variable, without introducing an overhead?
it just looks like a bug in older version of static analyzer, which couldn't detect fact that you assign values to the elements and were warning you about using an uninitialized class and message can't be avoided by compiler flags - disabling pedantic mode and all warning doesn't remove it, as res supposed to be a returned constant, it have to be initialized.
Simplest way to suppress it, would be to add value initialization, which would have no cost for compile-time initialization:
atype res {};
Also it looks like more idiomatic std::generate will be applicable here with C++20. Note - with C++17 your fill_array() isn't a constexpr due to returning atype res but you don't see a error because you allowed x to be runtime.
Proper get_array for C++17
static constexpr atype get_array()
{
atype res {};
for (std::size_t i = 0; i < size; i++)
res[i] = 1;
return res;
}
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.