I come across with a weird problem in which MSVC doesn't let me to use fold expression to initialize an array in the following:
#include <iostream>
template <typename T, std::size_t ...dims>
class Matrix {
public:
void print()
{
std::cout << (... + dims) << '\n';
}
T matrix[(... + dims)]; // <-- error C2059: syntax error: '...'
};
int main()
{
Matrix<int, 3, 3, 3> m;
m.print();
Matrix<int, 3, 2, 1> n;
n.print();
return 0;
}
Here is the errors:
(10): error C2059: syntax error: '...' (11): note: see
reference to class template instantiation 'Matrix' being
compiled (10): error C2238: unexpected token(s) preceding ';'
I tried GCC and everything just worked perfectly fine!
Is there any workaround to use fold expression directly to initialize an array with MSVC?
Thank you so much guys!
This looks like a bug in the MS compiler. As with any bug of such kind, it's hard to tell what exactly goes wrong unless you know MS compiler internals.
A workaround is the introduction of an intermediate member variable:
template<typename T, std::size_t... dims>
class Matrix {
// ...
static constexpr std::size_t my_size = (... + dims);
T matrix[my_size];
};
or a static member function:
static constexpr std::size_t my_size() { return (... + dims); }
T matrix[my_size()];
Related
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 am trying to write a function that takes an Eigen::Vector<T, dim> as a parameter. However, the following example fails to compile:
#include <Eigen/Core>
template<class F, typename T, int dim>
void bar(F&& func, const Eigen::Vector<T, dim>& arg1) {
}
template<typename T, int dim>
void foo(const Eigen::Vector<T, dim>& a) {
return bar([] {}, a);
}
int main() {
Eigen::Vector<float, 3> v1{ 1.f,2.f,3.f };
foo(v1);
return 0;
}
This, under Visual Studio 2019, gives me the following error:
1>main.cpp(9,10): error C2672: 'bar': no matching overloaded function found
1>main.cpp(14): message : see reference to function template instantiation 'void foo<float,3>(const Eigen::Matrix<float,3,1,0,3,1> &)' being compiled
1>main.cpp(9,1): error C2784: 'void bar(F &&,const Eigen::Matrix<T,dim,1,|_Rows==&&?:&&_Rows!=?:,_Rows,1> &)': could not deduce template argument for 'const Eigen::Matrix<T,dim,1,|_Rows==&&?:&&_Rows!=?:,_Rows,1> &' from 'const Eigen::Matrix<float,3,1,0,3,1>'
1>main.cpp(4): message : see declaration of 'bar'
My questions:
What is this weird |_Rows==&&?:&&_Rows!=?: in the error message?
What can I do to make the above code compile?
The bar function should have T and dim availabe. I cannot just take const AnyType& arg1, because the actual implementation of bar depends on compile-time known values T and dim.
I have seen https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html. I think I understand what they are saying, but I am not sure if it applies here. I am taking an actual Eigen::Vector as an argument, not an expression.
If there was an expression it would be fine for me, to have it materialized.
Nevertheless, if I try to follow their instruction and just use ArrayBase<Derived>, I lose the compile-time information about T and dim.
This indeed looks like an MSVC issue, it compiles fine with gcc >= 4.7, and clang >= 3.5: https://godbolt.org/z/kqoHyO
One possible workaround would be to explicitly write out what Eigen::Vector expands to:
template<class F, typename T, int dim>
void bar(F&& func, const Eigen::Matrix<T, dim, 1, 0, dim, 1>& arg1) {
}
https://godbolt.org/z/vlvSDP
The weird |_Rows==&&?:&&_Rows!=?: looks like MSVC mangled the default value of the Options template parameter:
AutoAlign |
( (_Rows==1 && _Cols!=1) ? Eigen::RowMajor
: (_Cols==1 && _Rows!=1) ? Eigen::ColMajor
: EIGEN_DEFAULT_MATRIX_STORAGE_ORDER_OPTION ),
If you want to get to the bottom of this, you should file a bug-report to the MSVC maintainers, maybe using a simplified example like this: https://godbolt.org/z/U_0Sh7 (probably it's possible to reduce this even more).
MSVC fails to compile
#include <iostream>
template<int N, int = N>
struct A;
template<int, int V>
struct A{static constexpr int VALUE = V;};
int main() {
A<1> a;
std::cout << a.VALUE;
}
with (3): error C2065: 'N': unknown identifier
(10): error C2975: 'V': invalid template argument 'A', constant expression expected (roughly translated).
clang compiles it silently.
So, the question: rotten code or demented MSVC?
MSVC version = VS 2019.
It was a bug in MSVC manifested up to compiler version 19.22, and fixed in 19.23. Demo: https://gcc.godbolt.org/z/occvKEfc3
(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.
I'm trying to implement a simple N-dimensional array. This seems to be working more or less properly but I just can't get its overloaded ostream operator work. Here's my current implementation:
#include <iostream>
#include <memory>
#include <vector>
template <typename Type, int Dimension>
struct Array {
typedef std::vector<typename Array<Type, Dimension - 1>::type> type;
template <typename cType, int cDimension>
friend std::ostream &operator<<(std::ostream &stream, const Array<cType, cDimension>::type &object) {
if (cDimension == 0) {
stream << object << ' ';
} else {
typedef typename Array<cType, cDimension>::type::iterator ArrayIterator;
for (ArrayIterator it = object.begin(); it != object.end(); ++it) {
typedef typename Array<cType, cDimension - 1>::type NestedArray;
NestedArray nArray = (NestedArray)(*it);
stream << nArray << std::endl;
}
}
return stream;
}
};
template <typename Type>
struct Array < Type, 0 > {
typedef Type type;
};
int main() {
Array<int, 1>::type c00 = { 1, 2, 3 };
Array<int, 1>::type c01 = { 2, 3, 4 };
Array<int, 1>::type c02 = { 3, 4, 5 };
Array<int, 2>::type c10 = { c00, c01 };
Array<int, 2>::type c11 = { c01, c02 };
Array<int, 3>::type c20 = { c10, c11 };
std::cout << c20 << std::endl;
return 0;
}
I'm getting the following compilation errors:
1>------ Build started: Project: NDepthArray, Configuration: Debug Win32 ------
1> Source.cpp
1>c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(10): warning C4346: 'Array<Type,Dimension>::type' : dependent name is not a type
1> prefix with 'typename' to indicate a type
1> c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(25) : see reference to class template instantiation 'Array<Type,Dimension>' being compiled
1>c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(10): error C2061: syntax error : identifier 'type'
1>c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(10): error C2805: binary 'operator <<' has too few parameters
1>c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(10): fatal error C1903: unable to recover from previous error(s); stopping compilation
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
I literally tried all my ideas already, which includes removing friend keyword and the actual class parameter but nothing changes. How can we overload operators for such class templates?
Cheers,
Joey
The problem with your approach is that the cType cannot be inferred:
template <typename Type, int Dimension> // Asking for Type as cType
struct Array {
typedef std::vector<typename Array<Type, Dimension - 1>::type> type;
}
template <typename cType, int cDimension> ↓↓↓↓↓
std::ostream &operator<<(std::ostream &stream, typename Array<cType, cDimension>::type &object)
Array<int, 3>::type c20 = { c10, c11 };
std::cout << c20 // Deduction failure
You can find more info here: https://stackoverflow.com/a/12566268/1938163
14.8.2.5/4
In certain contexts, however, the value does not participate in type
deduction, but instead uses the values of template arguments that were
either deduced elsewhere or explicitly specified. If a template
parameter is used only in non-deduced contexts and is not explicitly
specified, template argument deduction fails.
As a sidenote: implementing complex structures for multidimensional arrays or vectors with templated recursive code is a not-very-maintainable and surely hard-to-read path to achieve something that could have been done faster, more efficient (less allocations) and clearer with only a contiguous block of memory indexed in different strides.