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.
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.
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.
Yesterday I ran into a g++ (3.4.6) compiler problem for code that I have been compiling without a problem using the Intel (9.0) compiler. Here's a code snippet that shows what happened:
template<typename A, typename B>
class Foo { };
struct Bar {
void method ( Foo<int,int> const& stuff = Foo<int,int>() );
};
The g++ compiler error is:
foo.cpp:5: error: expected `,' or `...' before '>' token
foo.cpp:5: error: wrong number of template arguments (1, should be 2)
foo.cpp:2: error: provided for `template<class A, class B> struct Foo'
foo.cpp:5: error: default argument missing for parameter 2 of `void Bar::method(const Foo<int, int>&, int)'
Apparently, the default argument is not accepted when written this way, and the compiler assumes that instead of the second template argument a new function argument is specified, for which it then expects a default value because the stuff argument has one. I can help the compiler by creating a typedef, and then everything compiles fine:
template<typename A, typename B>
class Foo { };
struct Bar {
typedef Foo<int,int> FooType;
void method ( FooType const& stuff = FooType() );
};
So I can solve my problem, but I don't understand what is going on. Do I miss a C++ (template?) language feature here and am I doing something wrong, or is the g++ compiler wrong in not accepting the first piece of code?
Note BTW that this also compiles ...
template<typename A, typename B>
class Foo { };
void method ( Foo<int,int> const& stuff = Foo<int,int>() );
I am not so sure that this is a bug in g++ (since version 4.2.4). The code now passes in g++ 4.4 (see UPDATE below). In order to have this code compile for other versions of compilers you can add a set of parenthesis around the default argument:
template<typename A, typename B>
class Foo { };
struct Bar {
void method ( Foo<int,int> const& stuff = ( Foo<int,int>() ) );
};
IMO, these parenthesis are necessary as there is an additional requirement that the default argument can refer to a member of the class that may be declared later in the class body:
struct Bar {
void method ( int i = j); // 'j' not declared yet
static const int j = 0;
};
The above code is legal, and when the declaration for 'method' is being parsed the member 'j' has not yet been seen. The compiler therefore can only parse the default argument using syntax checking only, (ie. matching parenthesis and commas). When g++ is parsing your original declaration, what it is actually seeing is the following:
void method ( Foo<int,int> const& stuff = Foo<int // Arg 1 with dflt.
, int>() ); // Arg 2 - syntax error
Adding the extra set of parenthesis ensures that the default argument is handled correctly.
The following case shows an example where g++ succeeds but Comeau still generates a syntax error:
template<int J, int K>
class Foo { };
struct Bar {
void method ( Foo<0, 0> const & i = ( Foo<j, k> () ) );
static const int j = 0;
static const int k = 0;
};
EDIT:
In response to the comment: "You could just as well have a function call with multiple arguments there", the reason that this does not cause a problem is that the comma's inside the function call are surrounded in parenthesis:
int foo (int, int, int);
struct Bar {
void method ( int j =
foo (0, 0, 0) ); // Comma's here are inside ( )
};
It is possible therefore, to parse this using the syntax of the expression only. In C++, all '(' must be matched with ')' and so this is easy to parse. The reason for the problem here is that '<' does not need to be matched, since it is overloaded in C++ and so can be the less than operator or the start of a template argument list. The following example shows where '<' is used in the default argument and implies the less than operator:
template<int I, int J>
class Foo { };
struct Bar {
template <typename T> struct Y { };
void method ( ::Foo<0,0> const& stuff = Foo<10 , Y < int > = Y<int>() );
struct X {
::Foo<0, 0> operator< (int);
};
static X Foo;
};
The above "Foo<10" is a call to the "operator<" defined in 'X', not the start of the template argument list. Again, Comeau generates syntax errors on the above code and g++ (including 3.2.3) parse this correctly.
FYI, the appropriate references are a note in 8.3.6/5:
[Note: in member function declarations, names in default argument expressions are looked
up as described in 3.4.1...
and then in 3.4.1/8
A name used in the definition of a member function (9.3) of class X following the function’s declaratorid29)
shall be declared in one of the following ways:
...
— shall be a member of class X or be a member of a base class of X (10.2), or
This bullet here, is the part that forces the compiler to "delay" lookup for the meaning of the default argument until all of the class members have been declared.
<UPDATE>
As pointed out by "Employed Russian", g++ 4.4 is now able to parse all of these examples. However, until the DR has been addressed by the C++ standards committee I am not yet ready to call this a "bug". I believe that long term extra parenthesis will be required to ensure portability to other compilers/tools (and maybe even future versions of g++).
It has been my experience that the C++ standard does not dictate that compiler vendors should all use the same parser technology and they also cannot expect that all technologies are equally powerful. As a result, parsing requirements normally don't require that vendors perform superhuman feats. To illustrate this consider the following two examples:
typedef T::TYPE TYPE;
T::TYPE t;
If 'T' is dependent, then given each context 'TYPE' must be a typename, however the standard still requires the typename keyword. These examples are unambiguous and can only mean one thing, however the standard (in order to allow for all parser technologies) still requires the typename keyword.
It's possible that the DR may be addressed in such a way that a compiler which fails to parse these examples will still be "standard conforming" as long as extra parenthesis allows the code to parse.
</UPDATE>
This is a known bug in gcc.
It has been fixed in gcc-4.4, which compiles the original source just fine.
Looks like a compiler bug. I tried it on IBM's xlC compiler V7.0 (which I've found to be more standard-conforming than gcc) and it compiles okay.
template <bool> class A {};
typedef A<static_cast<bool>(1>0)> B;//buggy
int main() { B b; }
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.
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.[...]