I am using Visual Studio 2010 with SP1. The following code crashes the compiler:
template <typename T>
class MyClass
{
public:
typedef int my_int;
const my_int foo();
};
template <typename T>
const auto MyClass<T>::foo() -> my_int
// auto MyClass<T>::foo() -> const my_int // THIS WORKS!
{
return my_int(1);
}
int main()
{
MyClass<int> m;
m.foo();
}
Note the commented line that fixes the issue. Am I using auto properly here (i.e. const qualifier on auto)? Is the workaround essentially the exact same thing (i.e. can I safely use it until the compiler's bug is fixed)? And lastly, am I the only one experiencing this issue, if not, I will file a bug report.
NOTE: I realize that const here makes little sense. I was trying to replicate the bug in a smaller project where in the actual project I am returning a const reference to an object.
The code is ill-formed in C++11: if there is a trailing return type, then the "normal" return type must be auto (the C++11 specification states at 8.3.5[dcl.fct]/2 that "T shall be the single type-specifier auto," where T is the "type" that appears before the name of the function).
All compiler crashes are compiler bugs, so it is a bug that the Visual C++ 2010 compiler crashes when compiling your program. This bug has been fixed, though; Visual C++ 2013 rejects the program with a proper compilation error.
This is one of those cases where trying out code in multiple compilers may have helped you realize that using const auto with a trailing return type is an error. There are several online C++ compilers. If you had tried out this code in clang you would have received the following error(live example):
error: function with trailing return type must specify return type 'auto', not 'const auto'
The relevant section in the draft C++ standard is section 8.3.5 Functions paragraph 2 which says (emphasis mine):
In a declaration T D where D has the form
D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
ref-qualifieropt exception-specificationopt attribute-specifier-seqopt
trailing-return-type
[...]T shall be the single type-specifier auto.[...]
Related
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.
Code sample:
class A
{
static constexpr auto GetInt() noexcept { return 6; }
template<int N>
std::enable_if_t< N >= GetInt(), int> func() { return N; }
};
https://godbolt.org/z/-0pwIQ
Clang and MSVC both claim that GetInt() can't be used because it's not defined at that point, however GCC compiles with no errors or warnings.
My best guess for why the error occurs is that because the class is incomplete at the point that func(), member functions are considered undefined, and because auto relies on the function definition to deduce the return type, the compiler can't use it to generate a function signature.
However, that doesn't explain why GCC is allowing it. Is it incorrect to do so?
As it is already mentioned in the comments, this is one of C++ Standard Core Language Active Issues: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2335
It is still under discussion.
Though it does look like Clang/MSVC behavior might become standard:
Notes from the June, 2018 meeting:
The consensus of CWG was to treat templates and classes the same by
"instantiating" delayed-parse regions when they are needed instead of at the
end of the class.
This code compiles OK on g++ (Coliru), but not Visual C++ (rextester) - both online and my desktop.
It is a simplified version of a much larger Visual Studio 2015 project.
class AAA{
public: template<class T> static T* test(T* hqi){
return hqi;
}
};
class TTT3{
public: int d; //In real case, it is some class, but same error nonetheless.
decltype(AAA::test(&d)) dfd=AAA::test(&d); //<-- error only Visual C++
};
int main(){
int b;
decltype(AAA::test(&b)) dfd=AAA::test(&b); //OK for boths
}
'T *AAA::test(T *)': could not deduce template argument for 'T ' from
'int TTT3:: '
Question
Why? Is my code wrong? - I don't think so.
How to make it compile in Visual C++? I need it.
This is a Visual Studio specific bug. According to the standard:
[expr.unary.op/4]
A pointer to member is only formed when an explicit & is used and its
operand is a qualified-id not enclosed in parentheses. [ Note: That
is, the expression &(qualified-id), where the qualified-id is enclosed
in parentheses, does not form an expression of type “pointer to
member”. Neither does qualified-id, because there is no implicit
conversion from a qualified-id for a non-static member function to the
type “pointer to member function” as there is from an lvalue of
function type to the type “pointer to function” ([conv.func]). Nor is
&unqualified-id a pointer to member, even within the scope of the
unqualified-id's class. — end note ]
The text in bold is what VC++ doesn't do properly inside decltype for whatever reason. Since hoping that Microsoft will fix it is a fools hope, another workaround you can do is to add the following overload:
template<class C, typename T>
static T* test(T C::*);
Live Example
Possibly in a #ifdef/#endif block that checks for VC++. Not defining it prevents it being picked silently outside of an unevaluated context such as a decltype, albeit with only a link time error.
This doesn't look valid to me because the first &d in
decltype(AAA::test(&d)) dfd=AAA::test(&d);
is an implicit use of this outside of the member initializer. I can't find any exception in the Standard making an implicit use of this inside decltype valid, plus all three major compilers complain if you replace the &d with explicit &this->d.
Unfortunately, I don't see any easy way around this, unless you substitute the actual type of d or make a typedef for it.
It is my understanding that decltype is used to query the type of an objects/variables and so on.
From the examples present on wikipedia, such as the following:
int i;
decltype(i) x3; // type is int
I assumed I could do something like this:
class A
{
public:
int a, b;
};
template<typename T>
struct IsClass
{
enum { Yes = std::is_class<T>::value };
enum { No = !Yes };
};
std::vector<A> v;
auto it = v.begin();
IsClass<decltype(it)::value_type>::Yes
Because after all this line is legal:
IsClass<std::vector<A>::iterator::value_type>::Yes
Alas it wouldn't compile, citing the following: error C2039: 'value_type' : is not a member of 'global namespace''`
Any ideas as to why scope resolution was made to behave this way in presence of decltype?
P.S: If it makes any difference I'm using MSVC2012 (without the Nov CTP)
This is a known bug in the Visual C++ compiler. It has not yet been fixed as of the Visual C++ 2013 Preview. You can work around this issue using std::common_type:
IsClass<std::common_type<decltype(it)>::type::value_type>::Yes
^^^^^^^^^^^^^^^^^ ^^^^^^^
(std::common_type with a single template argument yields that argument type; it's the standardized C++11 equivalent of the identity template that has long been used in metaprogramming.)
You can find the public bug report on Microsoft Connect: Cannot use decltype before scope operator. If this issue is important to you, please consider upvoting that bug report.
It is my understanding that decltype is used to query the type of an objects/variables and so on.
From the examples present on wikipedia, such as the following:
int i;
decltype(i) x3; // type is int
I assumed I could do something like this:
class A
{
public:
int a, b;
};
template<typename T>
struct IsClass
{
enum { Yes = std::is_class<T>::value };
enum { No = !Yes };
};
std::vector<A> v;
auto it = v.begin();
IsClass<decltype(it)::value_type>::Yes
Because after all this line is legal:
IsClass<std::vector<A>::iterator::value_type>::Yes
Alas it wouldn't compile, citing the following: error C2039: 'value_type' : is not a member of 'global namespace''`
Any ideas as to why scope resolution was made to behave this way in presence of decltype?
P.S: If it makes any difference I'm using MSVC2012 (without the Nov CTP)
This is a known bug in the Visual C++ compiler. It has not yet been fixed as of the Visual C++ 2013 Preview. You can work around this issue using std::common_type:
IsClass<std::common_type<decltype(it)>::type::value_type>::Yes
^^^^^^^^^^^^^^^^^ ^^^^^^^
(std::common_type with a single template argument yields that argument type; it's the standardized C++11 equivalent of the identity template that has long been used in metaprogramming.)
You can find the public bug report on Microsoft Connect: Cannot use decltype before scope operator. If this issue is important to you, please consider upvoting that bug report.