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;
}
Related
Why this code snippet works with C++17 whereas the compiler complains when using C++11(i.e https://godbolt.org/z/71G91P)?
Are there any potential problems with this code snippet?
#include<iostream>
class ctx
{
public:
int map_create(void*){std::cout << "haha" << std::endl; return 0;};
};
ctx obj;
typedef int (ctx::*ctx_mem_func)(void*);
template <ctx_mem_func func>
int regHelper(void*)
{
((&obj)->*func)(nullptr);
return 0;
}
constexpr ctx_mem_func testFunc = &ctx::map_create;
typedef int(*callBackFunc)(void*);
int reg(callBackFunc)
{
return 0;
}
int main()
{
reg(regHelper<testFunc>);
//But this expression is ok.
reg(regHelper<&ctx::map_create>);
std::cout << "this is a test" << std::endl;
}
Here are the error messages when using c++11(gun 10.0.2):
<source>: In function 'int main()':
<source>:30:28: error: no matches converting function 'regHelper' to type 'callBackFunc {aka int (*)(void*)}'
reg(regHelper<testFunc>);
^
<source>:13:5: note: candidate is: template<int (ctx::* func)(void*)> int regHelper(void*)
int regHelper(void*)
^
This is a difference between C++14 and C++17. Simplified:
int f();
template<int (&)()> struct S {};
constexpr auto& q = f;
using R = S<q>; // valid in C++17, invalid in C++14
The change is to Allow constant evaluation for all non-type template arguments, meaning that now a constexpr variable naming a function (member function, etc.) is permissible as an NTTP where previously only the actual name of the function was permitted.
The code below compiles on MSVC but fails on GCC (4.6.3). Why does it fail and what should I do to fix it?
#include <array>
class Foo {
public:
template<typename T, int N>
operator std::array<T, N>() const {
return std::array<T, N>();
}
};
int main(){
Foo val;
// both of the following lines fail on GCC with error:
// "no matching function call...", ultimately with a note:
// "template argument deduction/substitution failed"
auto a = val.operator std::array<int, 2>();
static_cast<std::array<int, 2>>(val);
return 0;
}
EDIT: The following code, however, does compile (on both compilers), despite passing in an int for std::array's template parameter.
template<int N, typename T>
struct Bar {
std::array<T, N> buf;
};
int main()
{
auto x = Bar<3, double>();
return 0;
}
If you read the full text of the error messages you get, the compiler is complaining because the type for N in your template class is int, while the second parameter of std::array is std::size_t, which is an unsigned long on your system.
Changing your template's declaration to use std::size_t N will fix the problem.
MSVC is not complaining possibly because it recognizes that the value "2" works for either case, or because of a compiler bug.
To initialize an std::array of an arithmetic type AT at compile-time, I did this:
#include <array>
#include <iostream>
template<typename AT, auto DIM, auto N = 0>
constexpr void assign(std::array<AT, DIM>& arr, AT value)
{
arr[N] = value;
if constexpr (N < std::size(arr) - 1)
assign<AT, DIM, N + 1>(arr, value);
}
template<typename AT, auto DIM>
constexpr std::array<AT, DIM> zero_array()
{
std::array<AT, DIM> result;
assign(result, AT{0});
return result;
}
template<typename Container>
void print(Container&& cont)
{
for (const auto& el : cont)
std::cout << el << " ";
std::cout << std::endl;
}
int main()
{
auto zero10 = zero_array<double, 10>();
print(zero10);
}
I godbolted it, and it seems to me it worked:
However, when I compile it with
g++ -O3 -std=c++2a -Wall -Wpedantic -Wunused-parameter -I /usr/include main.cpp -o main
using g++ (GCC) 8.2.1 20181127, I get a "note"
In file included from main.cpp:1:
main.cpp: In instantiation of ‘constexpr std::array<AT, DIM> zero_array() [with AT = double; auto DIM = 10]’:
main.cpp:30:42: required from here
/usr/include/c++/8.2.1/array:94:12: note: ‘struct std::array<double, 10>’ has no user-provided default constructor
struct array
^~~~~
/usr/include/c++/8.2.1/array:110:56: note: and the implicitly-defined constructor does not initialize ‘double std::array<double, 10>::_M_elems [10]’
typename _AT_Type::_Type _M_elems;
Why is this note there? Can I ignore it? If so, how to get rid of it?
std::array does not have a default constructor. You need to use:
std::array<AT, DIM> result = {};
What's puzzling to me is why you think you need zero_array at all. If you use
std::array<double, 10> a = {};
you get a zero-initialized object.
(This is sort of an XY problem, but bear with me.)
I'm getting this compilation warning about a shift amount being too large. Now, to diagnose this, I would like my compiler to somehow emit the constexpr value which is used as a shift amount.
The way I've done it so far is try to instantiate a type with a numeric parameter which I know I can place out of range, then add the constexpr value I wanted and get an error which shows me the sum. But that's an ugly hack. Is there a way to get constexpr values (hopefully not only integers) to be emitted to the standard error stream? e.g. together with some explanatory text or a warning message?
I'm asking about GCC 6.x and later and clang 4.x and later.
Well, the obvious approach is similar to what you said -- make the compiler mention the value while emitting a diagnostic.
constexpr int I = 8 % 3;
template<int i>
class TheValueYouWantIs { static_assert(i != i); };
int main() {
TheValueYouWantIs<I>();
}
Thus:
prog.cpp: In instantiation of ‘class TheValueYouWantIs<2>’:
prog.cpp:8:27: required from here
[...less informative stuff...]
Warnings are obviously more compiler-dependent, but should be easily possible. This sort of thing won't help you with char arrays, though. Not a complete solution.
GCC displays <<'s operands when it issues an error message for overflow in a constant expression. It does not display <<'s operands when it only issues a warning message, when the result is not used as a constant expression. You can make use of this by adding an otherwise pointless constant.
template <int> constexpr int f() { return 1; }
template <int> constexpr int g() { return 40; }
template <int I> constexpr int h() { return f<I>() << g<I>(); }
int main() { h<1234>(); }
This causes a warning without information about the problematic value: warning: left shift count >= width of type [-Wshift-count-overflow].
template <int> constexpr int f() { return 1; }
template <int> constexpr int g() { return 40; }
template <int I> constexpr int h() { constexpr int i = f<I>() << g<I>(); return f<I>() << g<I>(); }
int main() { h<1234>(); }
This causes an error with information about the problematic value (along with some more warnings): error: right operand of shift expression ‘(1 << 40)’ is >= than the precision of the left operand.
If only the second operand is a constant expression, it's still fine, for this particular warning it suffices to turn the left operand into a constant 1.
This is super-ugly, but produces both the name of the expression and its value in a discernable, though terrible, format:
constexpr int I = 8 % 3;
#define CONCATENATE( s1, s2 ) s1 ## s2
#define EXPAND_THEN_CONCATENATE( s1, s2 ) CONCATENATE( s1, s2 )
template<int i>
class The_expression_named_in_the_previous_error_has_value{ static_assert(i != i, ""); };
#define ERROR_PRINT(_expr) \
EXPAND_THEN_CONCATENATE(In_the_next_error_you_will_find_the_value_of_the_expression__, _expr); \
The_expression_named_in_the_previous_error_has_value<I>();
int main() {
ERROR_PRINT(I);
}
This produces (with GCC 6):
main.cpp: In function ‘int main()’:
main.cpp:11:25: error: ‘In_the_next_error_you_will_find_the_value_of_the_expression__I’ was not declared in this scope
EXPAND_THEN_CONCATENATE(In_the_next_error_you_will_find_the_value_of_the_expression__, _expr); \
^
main.cpp:3:45: note: in definition of macro ‘CONCATENATE’
#define CONCATENATE( s1, s2 ) s1 ## s2
^
main.cpp:11:1: note: in expansion of macro ‘EXPAND_THEN_CONCATENATE’
EXPAND_THEN_CONCATENATE(In_the_next_error_you_will_find_the_value_of_the_expression__, _expr); \
^
main.cpp:15:5: note: in expansion of macro ‘ERROR_PRINT’
ERROR_PRINT(I);
^
main.cpp: In instantiation of ‘class The_expression_named_in_the_previous_error_has_value<2>’:
main.cpp:15:5: required from here
main.cpp:7:61: error: static assertion failed:
class The_expression_named_in_the_previous_error_has_value{ static_assert(i != i, ""); };
But I'm sure this can be massively improved upon with some constexpr-string-trickery.
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.