I ran across a problem upgrading templated array references to std::array where I had used this:
template<class T, int N>
void f(T(&a)[N]){/* do stuff */;}
This has worked in all compilers I have used so I just did this when changing to std::array
template<class T, int N>
void f(std::array<T,N>& a){/* do stuff */;}
And this worked fine on MSVC. But when I ran the code on other compilers it did not
The following code works in MSVC but not other compilers. See https://godbolt.org/z/d9cqWMeaM which fails to match because N must be std::size_t.
*This question indicates that SFINAE should apply. However, as noted in the comments and in spite of the that answer, SFINAE, typically applies to rejection of malformed declarations. This is a deduction failure.
Is MSVC's acceptance of this code a compiler bug? And is the T(&a)[N] where int N was used legal even though all compilers I've ever used had no problem at all?
This was somewhat annoying because some of the prior code used the fact N was signed in various places.
std::array size_type is specified as std::size_t.
GCC and Clang are right, template deduction should fail in this case, according to [temp.deduct.type]/18:
If P has a form that contains <i>, and if the type of i differs from the type of the corresponding template parameter of the template named by the enclosing simple-template-id, deduction fails.
. . .
[ Example:
template<int i> class A { /* ... */ };
template<short s> void f(A<s>);
void k1() {
A<1> a;
f(a); // error: deduction fails for conversion from int to short
f<1>(a); // OK
}
Note: in C++11 this rule was a little more human-readable, see here.
So technically MSVC's acceptance of the code is a bug. I would report it to the vendor (Help -> Send Feedback -> Report a Problem).
Related
The following code compiles with MSVC but fails with GCC and Clang for missing typename before a dependent type:
struct Foo { struct value{ }; };
struct Bar { int value; };
template<typename T>
constexpr size_t SIZE = sizeof(T::value); // missing typename here, for Foo
constexpr size_t s1 = SIZE<Foo>; // missing typename before dependent type above
constexpr size_t s2 = SIZE<Bar>;
MSVC approach, not requiring typename for sizeof, seems reasonable, as sizeof works on both types and variables. On the other hand GCC and Clang seem to play by the book, as this is one of those cases where you still need typename even in C++20, when the context cannot reveal to the compiler whether it is going to meet a type or a variable.
The question is whether MSVC is allowed to be permissive here, that is, if the compiler can perform the required operation correctly without the typename, is it allowed to do so? Or does it contradict the specification?
The difference between the approach of MSVC vs. Clang and GCC comes to an actual difference in the following code, which compiles by all three compilers but behaves differently:
template<typename T> concept A1 = sizeof(typename T::value) > 0;
template<typename T> concept A2 = sizeof(T::value) > 0;
struct Foo { struct value{ }; };
constexpr bool v1 = A1<Foo>; // true with all
constexpr bool v2 = A2<Foo>; // true with MSVC, false with GCC and Clang
In the above code, GCC and Clang see the omission of typename in A2 as illegal and thus fail the concept, this is not compilation error but rather having the concept getting the boolean value false ([temp.constr.atomic]).
C++20 spec ([temp.res.general]) lists the places where typename is not required for assuming a qualified-id is a type. The sizeof operator is not in that list, so it seems that it should require typename for a template dependent-type. On the other hand, sizeof doesn't appear in the example as ill-formed, no diagnostic required for missing typename (not being in the example does not say anything, but still keeps the question, in a way).
Why sizeof may allow inferring proper operation without adding typename? Mainly because it shouldn't really care if this is a type or a variable. And the spec does not say specifically that this is ill-formed. If it does say that it is ill-formed, then a pointer to this should be the answer, which will probably make MSVC behavior a defect.
As a side note, that does not answer the question, one can make all three compilers happy with the following code:
template<typename T>
constexpr size_t SIZE = sizeof(T::value);
template<typename T> requires requires { typename T::value; }
constexpr size_t SIZE<T> = sizeof(typename T::value);
And for the concept:
template<typename T> concept A =
sizeof(T::value) > 0 ||
sizeof(typename T::value) > 0;
The standard is very indirect about this point, but it’s pretty clear that MSVC is not conforming here. (In the non-SFINAE/concept case, it would be allowed to issue merely a warning (“operand of sizeof considered a type despite missing ‘typename’”) and continue, but that’s not really the point.)
Note that even in the trivial case of
struct X;
int y=X;
the error is that X can’t be interpreted as an unqualified-id because it is not “suitably declared” ([expr.prim.id.unqual]/1). In a template the grammatical interpretation is fixed by the presence of absence of typename, despite the fact that qualified-ids are thereby produced without knowing whether their terminal names are so suitably declared; we evidently must reject them if eventually they are found wanting in that regard.
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 tried to construct a case that requires no typename or template, but still yield a variable or template depending on whether a given name t is a function parameter pack or not
template<typename T> struct A { template<int> static void f(int) { } };
template<typename...T> struct A<void(T...,...)> { static const int f = 0; };
template<typename> using type = int;
template<typename T> void f(T t) { A<void(type<decltype(t)>...)>::f<0>(1); }
int main() {
f(1);
}
The above will refer to the static const int, and do a comparison. The following just has T t changed to be a pack and make f refer to a template, but GCC does not like either
template<typename ...T> void f(T ...t) { A<void(type<decltype(t)>...)>::f<0>(1); }
int main() {
f(1, 2, 3);
}
GCC complains for the first
main.cpp:5:68: error: incomplete type 'A<void(type<decltype (t)>, ...)>' used in nested name specifier
template<typename T> void f(T t) { A<void(type<decltype(t)>...)>::f<0>(1); }
And for the second
main.cpp:5:74: error: invalid operands of types '<unresolved overloaded function type>' and 'int' to binary 'operator<'
template<typename ...T> void f(T ...t) { A<void(type<decltype(t)>...)>::f<0>(1); }
I have multiple questions
Does the above code work according to the language, or is there an error?
Since Clang accepts both variants but GCC rejects, I wanted to ask what compiler is correct?
If I remove the body of the primary template, then for the f(1, 2, 3) case, Clang complains
main.cpp:5:42: error: implicit instantiation of undefined template 'A<void (int)>'
Please note that it says A<void (int) >, while I would expected A<void (int, int, int)>. How does this behavior occur? Is this a bug in my code - i.e is it illformed, or is it a bug in Clang? I seem to remember a defect report about the order of expansion vs the substitution of alias template, is that relevant and does it render my code ill-formed?
Expanding a parameter pack either should, or does, make an expression type dependent. Regardless of whether the things expanded are type dependent.
If it did not, there would be a gaping hole in the type dependency rules of C++ and it would be a defect in the standard.
So A<void(type<decltype(t)>...)>::f when t is a pack, no matter what tricks you pull in the void( here ) parts to unpack the t, should be a dependent type, and template is required before the f if it is a template.
In the case where t is not a pack, it is intended that type<decltype(t)> not be dependent (See http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1390), but the standard may or may not agree at this point (I think not?)
If compilers did "what the committee intended", then when t is not a pack:
A<void(type<decltype(t)>...)>::f<0>(1)
could mean
A<void(int...)>::f<0>(1)
which is
A<void(int, ...)>::f<0>(1)
and if f is a template (your code makes it an int, but I think swapping the two should work) this would be fine. But the standard apparently currently disagrees?
So if http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1390 was implemented, then you could swap your two A specializations. The void(T...,...) specialization should have a template<int> void f(int), and the T specialization should have a static const int.
Now in the case where A<> is dependent (on the size of a pack), ::f is an int and does not need template. In the case where A<> is not dependent, ::f is a template but does not need disambiguation.
We can replace the type<decltype(t)>... with:
decltype(sizeof(decltype(t)*))...
and sizeof(decltype(t)*) is of non-dependent type (it is std::size_t), decltype gives us a std::size_t, and the ... is treated as a old-school ... arg. This means void(std::size_t...) becomes a non-dependent type, so A<void(std::size_t...)> is not dependent, so ::f being a template is not a template in a dependent context.
In the case where t is a parameter pack with one element
decltype(sizeof(decltype(t)*))...
becomes
std::size_t
but in a dependent context (one copy per element in t pack). So we get
A<void(std::size_t)>::f
which is presumed to be a scalar value, so
A<void(std::size_t)>::f<0>(1)
becomes an expression evaluating to false.
(Chain of logic generated in a discussion with Johannes in comments in original question).
Your second case is ill-formed; A<void(type<decltype(t)>...)>::f<0>(1) should be
A<void(type<decltype(t)>...)>::template f<0>(1)
// ~~~~~~~~~
For the first case, both compilers are behaving incorrectly; this was considered sufficiently confusing that CWG 1520 was raised to query the correct behavior; the conclusion was that pack expansion should be applied before alias substitution:
The latter interpretation (a list of specializations) is the correct interpretation; a parameter pack can't be substituted into anything, including an alias template specialization. CWG felt that this is clear enough in the current wording.
This is reminiscent of CWG 1558 (alias templates and SFINAE), which was fixed for C++14, but per the above even C++11 compilers are expected to get this correct, so it is disappointing that gcc and clang get it wrong (though in fairness they do behave correctly in simpler cases, including the motivating example in CWG 1520). Note that MSVC had a similar bug till recently; it is fixed in VS2015.
Your code (only in the first case) is correct; but as a workaround, you could alter your alias template to use and discard its template parameter, fixing your program for both compilers - of course that means that your CWG 1390 exploit will cease to be valid:
template<typename T> using type = decltype(((int(*)(T*))(0))(0)); // int
However, I don't think your CWG 1390 trick can work as presented, since even though the expansion-substitution of type<decltype(t)>... is not dependent on the types of t..., it is dependent on their number:
template<typename T> struct A { template<int> static void f(int) {} };
template<> struct A<void(int, int, int)> { static const int f = 0; };
As Yakk points out, it can be made to work if you swap the member function template and data member, since a data member is OK in dependent context.
This is more of a c++ standards question.
Consider the following code:
template <typename T>
class has_Data
{
typedef char one;
typedef long two;
template <typename C> static one test( typeof(&C::Data) ) ;
template <typename C> static two test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
class MyClass {
private:
struct Data {
};
};
void function(bool val = has_Data<MyClass>::value) {}
The above code works with gcc (GCC) 4.4.3
However with clang version 3.3 (2545b1d99942080bac4a74cda92c620123d0d6e9) (2ff97832e593926ea8dbdd5fc5bcf367475638a9)
it gives this error:
test_private_data.cpp:7:54: error: 'Data' is a private member of 'MyClass'
template <typename C> static one test( typeof(&C::Data) ) ;
^
/devshared/home/rhanda/test_private_data.cpp:7:37: note: while substituting explicitly-specified template arguments into function template 'test'
template <typename C> static one test( typeof(&C::Data) ) ;
^
/devshared/home/rhanda/test_private_data.cpp:21:26: note: in instantiation of template class 'has_Data<MyClass>' requested here
void function(bool val = has_Data<MyClass>::value) {}
^
1 error generated.
Which one is right?
From standard document (n3485), I found a statement which seems to agree with clang more than gcc.
Access control is applied uniformly to all names, whether the names are referred to from declarations or expressions.
I would assume that GCC is right.
The first thing to note is that no non-friend code should be able to positively report the existence of a given private member. So if that is what you try to do, you have to modify your design. A class can do anything with its private members, and other code (excepting friends) should have no way to know about it. That's by design.
However, there is the SFINAE principle: substitution failure is not an error. Since MyClass::Data is private, the code in has_Data should – in my opinion – act as if there was no C::Data member at all. Hence the first function would lead to a substitution failure, which gets silently ignored, and the second function is the one used. Adding a bit more code, my GCC 4.7.2 compiles this without issues and with has_Data<MyClass>::value evaluating to false. Correct SFINAE in my opinion.
Trying to back this opinion up with a quotation from the document you referred to, I found the following in section 14.8.2 paragraph 8:
Note: Access checking is done as part of the substitution process.
This is a non-normative note in the standard, but to me appears to be a very readable and clear indication that SFINAE should in fact apply in this situation, just the way GCC handles it.
Edit: As #hvd pointed out in a comment, the above is only true for C++11. In older versions of the standard, the situation used to be different. Issue 1170: Access checking during template argument deduction has details on that change.
GCC will not compile this code with -std=c++03 or -std=c++11 due to the fact that typeof is a GNU extension. The fact that -std=gnu++03 still compiles the code might perhaps be considered inappropriate, but since the way forward is using the C++11 semantics, I wouldn't bother filing a report about this.
Please consider this code:
template<typename T>
char (&f(T[1]))[1];
template<typename T>
char (&f(...))[2];
int main() { char c[sizeof(f<void()>(0)) == 2]; }
I expected it doing SFINAE and chosing the second overload, since substitution of T into T[1] yields
void [1]()
Which is an invalid type, of course. Adjustment of parameter types (array->pointer) is done after substituting template parameters into function parameters and checking for valid resulting types like 14.8.2 [temp.deduct] describes.
But both comeau and GCC fail to compile the above. Both with different diagnostics.
Comeau says:
"ComeauTest.c", line 2: error: array of functions is not allowed char (&f(T[1]))[1];
GCC says (version 4.3.3):
error: ISO C++ forbids zero-size array c
Meaning, GCC does not fail to substitute, but it chooses the first overload of f, returning a sizeof of 1, instead of failing to substitute it up front like Comeau.
What compiler is right and is my code valid at all? Please refer to or quote the proper Standard section in your answer. Thanks!
Update: The Standard itself contains such an example in the list at 14.8.2/2. I don't know, why I overlooked it first:
template <class T> int f(T[5]);
int I = f<int>(0);
int j = f<void>(0); // invalid array
While the example is only informative, it shows the intention of all those mysterious paragraphs and seems to show the code above should work and reject the first overload.
A small note, although very rare, I have found some occasions where I
believe that the Comeau compiler has it wrong - although, these
occasions are so rare that its always worth double and triple
checking your assumptions!
I may have a reason for the behaviour of g++. I'm not sure its
specified exactly when parameter types are adjusted:
Consider the following:
template<typename T>
struct A
{
void bar (T[10]);
};
template<typename T>
void A<T>::bar (T*)
{
}
The definition of 'bar' is legal, as "T[10]" decays to "T*". I do
not see anything in the standard that prohibits the compiler from
performing the adjustments of 8.3.5 against the template declaration,
and it also improves performance when it comes to overload matching.
Applying this to your example, g++ might be treating it as:
template<typename T>
char (&f( T* ))[1];
template<typename T>
char (&f(...))[2];
int main() { char c[sizeof(f<void()>(0)) == 2]; }
In the above, the substituted parameter is a legal pointer to
function, rather than an array of functions.
So, the question for me is - is if there is something that prohibts
the adjustments for the function parameters (8.3.5) twice?
Personally, I think it makes sense to allow the adjustments to happen
twice since otherwise it complicates the matching of function template
overloads
In conclusion, I think its valid for g++ to select the first overload
based on how it treates decaying array parameters, and Comeau is wrong
not to have a deduction failure for the array of functions.
Of course this now means that (if Comeau was fixed) then each compiler
would choose a different overload and would still be standards
compliant! :(
EDIT:
Just to illustrate my point, consider the following code:
template <typename T> void foo ( T * );
template <typename T> void foo ( T * const );
template <typename T> void foo ( T [] );
template <typename T> void foo ( T [10] );
template <typename T> void foo ( T [100] );
void bar ()
{
foo < void() > ( 0 );
}
Here, foo has been declared and redeclared several times. Which declaration, and so which parameter type, should the compiler apply the rules listed in 14.8.2?
My point is that the standard doesn't say anything about the above. I would also go as far as to say that any wording on this would have to leave it as either "undefined" or "implementation defined" behaviour.
Suprisingly enough - this does work in VS2008. I don't think that's necessarily evidence for it being correct behaviour or not though...
Visual Studio is interpretting
char (&f(T[1]))[1];
as a function that takes an array of size 1 of T, and returns a reference to an array of chars of size 1.
I'll try to describe the process of template argument deduction as I understand it from reading the standard.
Explicit template arguments are checked as described in 14.8.2/2.
The resulting function signature is adjusted as per 8.3.5 (i.e. array to pointer decay is performed).
Implicit template arguments are deduced as per 14.8.2.1 (this is performed on a partially substituted signature from step 2).
The deduction for the first overload fails in step 1, the overload resolution therefore returns the second overload. I don't believe the program is ill-formed.