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.
Related
I am learning C++ using the resources listed here. In particular, I have learnt that in C++20 we can have a class type as a non type template parameter. Now, to better understand the concept, I tried the following example that is accepted by msvc and gcc but rejected by clang. My question is which compiler is right?
Demo
struct Impl
{
constexpr Impl(std::initializer_list<int>)
{
}
};
struct Bar{};
template<typename T, Impl impl>
struct Foo
{
};
int main()
{
constexpr Foo<Bar, {1,2,3,4}> foo; //works in msvc & gcc but rejected in clang
return 0;
}
GCC and MSVC are wrong in accepting the program as it is ill-formed for the reason explained below.
The standard doesn't allow braced init list {1,2,3,4} to be a template argument. This can be seen from temp.names#1:
template-argument:
constant-expression
type-id
id-expression
And since {1,2,3,4} is not any of the above three listed constructs, it cannot be used as a template argument.
Additionally note that {1,2,3,4} is not an expression and does not have a type.
This is the reason clang generates the error saying:
vvvvvvvvvvvvvvvvvvv
error: expected expression
constexpr Foo<Bar, {1,2,3,4}> foo;
The gcc bug has been reported as:
GCC accepts invalid program involving {1,2,3,4} as template argument
And msvc bug as:
MSVC accepts invalid program involving {1,2,3,4} as template argument
In the following code there is an initialization of A<T> objects with template argument deduction using two forms distinct by the type of braces:
template<typename T>
struct A{ T x; };
int main() {
static_assert( A{1}.x == 1 ); //#1: ok in GCC and MSVC
static_assert( A(1).x == 1 ); //#2: ok in GCC only
}
The first way is accepted by both GCC and MSVC, while the second one is ok for GCC only while MSVC prints errors:
error C2641: cannot deduce template arguments for 'A'
error C2780: 'A<T> A(void)': expects 0 arguments - 1 provided
error C2784: 'A<T> A(A<T>)': could not deduce template argument for 'A<T>' from 'int'
Demo: https://gcc.godbolt.org/z/97G1acqPr
Is it a bug in MSVC?
This is a bug in MSVC.
The following papers were all introduced in C++20:
P0960R3: Allow initializing aggregates from a parenthesized list of values
P1975R0: Fixing the wording of parenthesized aggregate-initialization
P2131R0: Fixing CTAD for aggregates
Whilst MSVC lists them all as implemented in their Microsoft C/C++ language conformance by Visual Studio version pages, it seems whilst they have correctly implemented them in isolation
// OK (P0960R3, P1975R0)
struct A { int x; };
A a(1);
// OK (P2131R0)
template<typename T>
struct B { T x; };
B b{1};
// rejects-invalid (allowed by the union of the papers)
template<typename T>
struct C { T x; };
C c(1);
MSVC seems to have missed implementing the union of the papers. I have not, however, been able to find an open bug report.
These lines being well-formed relies on an aggregate deduction candidate, which provides a way for T to be deduced from aggregate intialization lists. This feature is indifferent to the syntax, so failing on either one but not the other is already inconsistent.
In MSVC's case, it's the combination of class template parameter deduction and parenthesized aggregate intialization that is causing the issue (the latter works fine in isolation). MSVC also fails to compile A a(1);, which is more obviously well-formed.
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.
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.
I believe that I have found a problem with gcc's alias template handling. Essentially, gcc appears to fail to correctly substitute the alias's template-id for an alias template instantiation when referring to the types by reference.
I was able to whittle a messy real-world problem down to a minor variation on the non-normative example provided in the C++ 11 standard section temp.alias (14.5.7/2):
#include <vector>
using namespace std;
template <class T>
using Vec = vector<T, allocator<T>>;
template <template <class> class TT>
void f1(TT<int> v);
template <template <class> class TT>
void f2(TT<int>& v);
template <template <class, class> class TT>
void g1(TT<int, allocator<int>> v);
template <template <class, class> class TT>
void g2(TT<int, allocator<int>>& v);
void foo()
{
Vec<int> v;
f1(v); // gcc and clang both correctly yield no matching function error
g1(v);
f2(v); // clang yields a no matching function error
g2(v); // gcc yields a no matching function error
}
As noted above, clang 3.3 (recent pull from svn) and gcc (4.7.2, 4.8.0, and 4.8.1) agree on the handling of f1/g1 in conformance with the standard, but differ on the handling of f2/g2 (to be clear, all tested versions of gcc accept the call to f2() and error on the call to g2()). The difference between f1/g1 and f2/g2 is of course that the latter pair uses a reference parameter.
All indications, both in this example and in my real problem, are that gcc is not correctly converting the type of the instantiation of the alias template (e.g. Vec<int>) to the aliased type (e.g. vector<int, allocator<int>>) prior to trying to deduce the template parameter for the instantiations of f2 and g2.
My question is: first, is indeed gcc incorrect and clang correct here, and second, is there any straightforward way (other than not using the alias template) to convince gcc to reject f2 and match g2.
Indeed this is a GCC bug. One workaround is to simply add a typedef.
typedef Vec<int> vit;
vit v;
Given that this is effective, an identity metafunction would probably also work.
It looks like the compiler has issue with the inference. Explicitly invoking the template enables a work around -- I can't speak to why gcc fails to infer as well as icpc or clang.
For icpc (ICC) 13.1.0 20130121:
f1<Vec> (v); // gcc and clang both correctly yield no matching function error
g1(v);
f2<Vec>(v); // clang yields a no matching function error
g2(v); // gcc yields a no matching function error
For gcc (GCC) 4.7.2 20121109 (Red Hat 4.7.2-8):
f1<Vec> (v); // gcc and clang both correctly yield no matching function error
g1(v);
f2<Vec>(v); // clang yields a no matching function error
g2<vector>(v); // gcc yields a no matching function error