I am trying to implement a templated C-array with specialization as the following:
// template definition
template< int a, int b > constexpr int arr[] = { 1 };
// partial specialization, works ok
template< int b > constexpr double arr<0, b>[] = { 2.0 };
// full specialization, compile error in MSVC, ok in g++
template< > constexpr float arr<1, 0>[] = { 3.0f };
I am using MSVC compiler with Visual Studio 2017, with the C++ standard set to C++17, and the compiler complains that C2133: 'arr<1,0>': unknown size, so adding the size 1 to the full specialization resolves the error. However, it compiles under Ubuntu g++ 8.1.0 with -pedantic flag on.
In my opinion, full specialization on functions and classes acts as if a non-template version is defined, so I guess this should also apply to the variable template, and the full specialization above could be equivalent to (except for the name)
constexpr float arr_with_a1_and_b0[] = { 3.0f };
which looks pretty valid to me, since the size should be deduced from the list-initialization (aggregate-initialization).
My question is: Is the code above valid C++? Which compiler is correct?
This was due to a bug with MSVC compiler: https://developercommunity.visualstudio.com/t/compiler-error-c2133-unknown-size-for-constant-tem/228098.
As of MSVC with Visual Studio 16.10.1, this problem has been fixed.
Related
According to P1814R0, the template deduction should work for alias with default value. With GCC 12.2(-std=c++20), the following code built successfully. However, in MSVC v19.33(/std:c++20) (which supports P1814R0), I got an error
<source>(10): error C2641: cannot deduce template arguments for 'Matrix3'
Is this a MSVC bug or I missed some configurations in MSVC?
Test codes:
template <typename Type, int Row, int Col, int Options = 0>
class Matrix {
Type storage[Row * Col];
};
template <typename Type = double>
using Matrix3 = Matrix<Type, 3, 3>;
int main() {
Matrix3 a;
return 0;
}
https://godbolt.org/z/nbfaxY7vs
The syntax for saying: I don't want to provide template arguments, just use the defaults, should be:
Matrix3<> a;
Indeed C++20 adopted P1814 into section over.match.class.deduct, so it seems that the following should be valid since C++20:
Matrix3 a;
GCC
As the OP mentions in a comment GCC rejected the above in C++17 and accepts it in C++20.
MSVC
As mention by #康桓瑋 MSVC accepts since C++20 only the form:
Matrix3 a{};
but still rejects:
Matrix3 a;
Clang
Clang still rejects both.
To Summarize
It seems that GCC is updated for C++20 on that respect, MSVC did part of the way and Clang is lagging behind.
I have a question about a C++ operator overloading on template class and type which is not correctly resolved by Microsoft Visual C++ while it is accepted by gcc and clang (Linux and macOS).
I suspect that this is a bug in MSVC++ but I would like to get the advice of experts before reporting the bug, in case it could be the opposite (gcc and clang being wrong).
The idea is a template class which must be instantiated with some integer type. We want to define the addition with any other plain integer types, both ways (class + int and int + class).
The minimal code for which I can reproduce the issue is below:
#include <type_traits>
template <typename INT, typename std::enable_if<std::is_integral<INT>::value>::type* = nullptr>
class A
{
public:
template<typename INT2>
A operator+(INT2 x) const { return A(); }
};
template <typename INT1, typename INT2>
A<INT2> operator+(INT1 x1, A<INT2> x2) { return x2 + x1; }
int main(int argc, char* argv[])
{
typedef A<int> B;
B x, y;
y = x + 1; // ok everywhere
y = 1 + x; // VC++: error C2677: binary '+': no global operator found which takes type 'B' (or there is no acceptable conversion)
}
In the original code, there are SFINAE constructs everywhere to enforce type checking when necessary (including in the two "+" operators). I have removed all of them when they did not change the compilation error. Only the "enable_if" in the definition of class A is required. Without it, the code compiles ok with MSVC++.
Microsoft Visual Studio 2019, version 16.9.3.
What is wrong here? MSVC++ or gcc/clang?
Thanks for your advices.
If we're being pedantic, in C++17, non-type template arguments cannot have type void*, see [temp.param]/4.2:
A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
. . .
— pointer to object or pointer to function,
. . .
(Note: it has been rectified in C++20).
So it seems something goes wrong in MSVC (the SFINAE succeeds in A itself but causes the lookup of the operator to fail). MSVC doesn't support C++20 fully yet, but it may still be worth reporting the issue.
For more portable code, use SFINAE based on int instead of void*:
template <typename INT, typename std::enable_if<std::is_integral<INT>::value, int>::type = 0>
class A {
. . .
This compiles OK in MSVC 16.9.
Consider this code:
#define SOLUTION 0
template <class T>
constexpr int one = 1;
template <class T>
struct A {
static constexpr int o = one<A<T>>;
void call() {
static_assert(one<A<T>> == 1, "Failure");
}
};
int main() {
#if SOLUTION
A<int> object;
#endif
[](A<int> a) {
a.call();
};
return 0;
}
It builds successfully here on ideone, regardless of the value of SOLUTION define.
Now, I know that this code makes little practical sense, but that is because I was trying earnestly to find the minimal working example of this strange behaviour. If I build this with the latest Visual Studio 2017 (platform toolset v141, _MSC_VER = 1910), I get the following error:
1>source.cpp(11): error C2131: expression did not evaluate to a constant
1>source.cpp(11): note: failure was caused by a read of an uninitialized symbol
1>source.cpp(11): note: see usage of 'one<A<int>>'
1>source.cpp(10): note: while compiling class template member function 'void A<int>::call(void)'
1>source.cpp(21): note: see reference to function template instantiation 'void A<int>::call(void)' being compiled
1>source.cpp(21): note: see reference to class template instantiation 'A<int>' being compiled
Funnily enough, when I put 1 for the SOLUTION macro, it builds successfully. The only difference is that an unused A<int> object; variable is defined in main's scope before the compiler gets to the lambda definition.
It is also perfectly fine if, before main, I define a function like this:
void f(A<int>& a) {
a.call();
}
In fact it solves the problem equally well as does the variable definition.
Is it a compiler bug or is the above chunk of code in violation of the standard?
Quoting Ajay:
Compiles on VS2017 update 3, but not on (RTM) update 2
I've confirmed this behaviour on my machine as well. This means that the relevant corrections exist and will be introduced soon enough to the production version itself, so I believe that this bug is unworthy of further notice.
Visual Studio 2013 (update 2) throws a compile-time error when compiling a template function who's return type is a nested type name, which has been hidden through multiple inheritance, and made visible again with the using keyword; as in the following code:
struct Base1
{
typedef int value_type;
};
struct Base2
{
typedef double value_type;
};
struct Derived : Base1, Base2
{
using Base1::value_type;
};
template<typename T>
typename T::value_type nullary_function() { return 0; }
template<typename T>
typename T::value_type unary_function(T t) { return 0; }
int main()
{
nullary_function<Derived>(); // Error: C2770
unary_function( Derived() ); // Error: C2893
return 0;
}
(The error numbers vary depending upon whether the function accepts template arguments or not as shown in the comments.)
G++ 4.7 accepts this code.
Specifically, I would like to know what the C++ standard has to say on the matter and whether or not this is a VC++ compiler bug. (It would appear to me that it is seeing as making nested types visible with the using keyword makes them visible in every other situation as far as I'm aware.)
I am also aware that the line with the using keyword may be changed from
using Base1::value_type;
to
typedef Base1::value_type value_type;
in order to get the code to compile and function correctly, but it seems bad for portability for some (potentially) valid code to compile on some compilers and not others - hence the desire for clarification.
This is indeed a compiler bug -- ICC, CLang, and G++ all accept this code as verified on Godbolt.
The most applicable language in the standard I could find is 7.3.3 (namespace.udecl) p2 (quoted from N3337)
Every using-declaration is a declaration and a member-declaration and so can be used
in a class definition.
P.S. ICC, CLang, and G++ all accept the typedef version as well.
Microsoft VC++ 2010 gives an error on this code:
template <int D, typename T>
void Foo(T x[D]) {
// details omitted
}
int main() {
float x[3];
Foo(x); // C2784: could not deduce template argument for 'T [D]' from 'float [3]'
return 0;
}
The same code passes muster with gcc and clang.
Is this a bug with VC++ 2010?
If it is a bug:
Does anyone know if it's been fixed in a later version of VC++?
Is there a workaround besides explicitly calling Foo<3, float>?
If it is not a bug:
Is there an extension to gcc and clang that allows them to resolve the template arguments?
I've greatly simplified the actual code down to this small example. I've tried it on other compilers, but I don't presently have access to newer Microsoft compilers. I've found similar questions on SO, but none that specifically address this case.
A parameter of type T x[D] is equivalent to T x[] aka T* x. D cannot be deduced from it. Make it void Foo(T (&x)[D]) - you are passing a reference to an array this way.