Welcome to MSDN Blogs Sign in | Join | Help

decltype: C++0x Features in VC10, Part 3

Part 1 of this series covered lambdas, auto, and static_assert.

 

Part 2 of this series covered rvalue references, which enable move semantics and perfect forwarding.

 

Today, I'm going to talk about decltype, which allows perfect forwarding functions to have arbitrary return types.  It's of interest to people who are writing highly generic code.

 

 

the return type problem

 

C++98/03 has an interesting blind spot - given an expression like x * y, where x and y have arbitrary types, there's no way to say "the type of x * y".  If x is of type Watts and y is of type Seconds, then x * y might be of type Joules.  Given print(const T& t), you can call print(x * y), and T will be deduced to be Joules, but this doesn't work in reverse: when writing multiply(const A& a, const B& b), you can't name its return type while preserving full generality.  Even though when multiply<A, B>() is instantiated, the compiler knows the type of x * y, that information is unavailable to you here.  The C++0x keyword decltype removes this blind spot, allowing you to say "multiply() returns the type of x * y".  (decltype is an abbreviation of "declared type"; I pronounce it as rhyming with "speckle type".)

 

 

decltype: the pattern

 

Here's how to write a completely generic functor that wraps operator+().  This Plus functor is not a template, but it has a templated function call operator that takes two arguments of arbitrary (and possibly different) types, adds them together, and returns the result, which can be of arbitrary (and possibly different from both of the arguments) type.

 

C:\Temp>type plus.cpp

#include <algorithm>

#include <iostream>

#include <iterator>

#include <ostream>

#include <string>

#include <utility>

#include <vector>

using namespace std;

 

struct Plus {

    template <typename T, typename U>

    auto operator()(T&& t, U&& u) const

    -> decltype(forward<T>(t) + forward<U>(u)) {

        return forward<T>(t) + forward<U>(u);

    }

};

 

int main() {

    vector<int> i;

    i.push_back(1);

    i.push_back(2);

    i.push_back(3);

 

    vector<int> j;

    j.push_back(40);

    j.push_back(50);

    j.push_back(60);

 

    vector<int> k;

 

    vector<string> s;

    s.push_back("cut");

    s.push_back("flu");

    s.push_back("kit");

 

    vector<string> t;

    t.push_back("e");

    t.push_back("ffy");

    t.push_back("tens");

 

    vector<string> u;

 

    transform(i.begin(), i.end(), j.begin(), back_inserter(k), Plus());

    transform(s.begin(), s.end(), t.begin(), back_inserter(u), Plus());

 

    for_each(k.begin(), k.end(), [](int n) { cout << n << " "; });

    cout << endl;

 

    for_each(u.begin(), u.end(), [](const string& r) { cout << r << " "; });

    cout << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 plus.cpp

plus.cpp

 

C:\Temp>plus

41 52 63

cute fluffy kittens

 

Compare this to C++98/03 <functional>'s std::plus<T> (which is unchanged in C++0x).  Because it's a class template, you'd have to pass plus<int>() and plus<string>(), repeating the element types.  Its non-templated function call operator has the form T operator()(const T& x, const T& y) const, making it unable to deal with 2 different types, much less 3 different types, without resorting to implicit conversions.  (You can feed plus<string>() a string and a const char *.  That will construct a temporary string from the second argument, before concatenating the two strings.  The performance of this is not especially desirable.)  Finally, because it takes const T&, it can't take advantage of C++0x move semantics.  Plus avoids all of this: Plus() doesn't repeat the element type, it deals with the "3 different types" case, and because it uses perfect forwarding, it respects move semantics.

 

 

trailing return types

 

Now, let's look at that templated function call operator again:

 

template <typename T, typename U>

auto operator()(T&& t, U&& u) const

-> decltype(forward<T>(t) + forward<U>(u)) {

    return forward<T>(t) + forward<U>(u);

}

 

Here, auto has a different meaning from for (auto i = v.begin(); i != v.end(); ++i), where it says "make the type of this thing the same as the type of whatever initializes it".  When used as a return type, auto says "this function has a trailing-return-type; after I declare its parameters, I'll tell you what its return type is".  (The C++0x Working Draft N2857 calls this a late-specified return type, but this is being renamed to trailing-return-type; see paper N2859.)  If this seems suspiciously similar to how lambdas are given explicit return types, that's because it is.  A lambda's return type has to go on the right in order for its lambda-introducer [] to appear first.  Here, the decltype-powered return type has to go on the right in order for the function parameters t and u to be declared first.  Where the auto appears on the left, the template parameters T and U are visible, but the function parameters t and u are not yet visible, and that's what decltype needs.  (Technically, decltype(forward<T>(*static_cast<T *>(0)) + forward<U>(*static_cast<U *>(0))) could go on the left, but that's an abomination.)

 

As for the expression given to decltype, giving it the same expression as the return statement ensures correctness in all cases.  (Pop quiz: why would decltype(t + u) be wrong?)  The repetition here is unavoidable but centralized - it appears exactly once, on adjacent lines, so it is not dangerous.

 

 

another example

 

For completeness, here's that "3 different types" example:

 

C:\Temp>type mult.cpp

#include <algorithm>

#include <iostream>

#include <iterator>

#include <ostream>

#include <utility>

#include <vector>

using namespace std;

 

struct Multiplies {

    template <typename T, typename U>

    auto operator()(T&& t, U&& u) const

    -> decltype(forward<T>(t) * forward<U>(u)) {

        return forward<T>(t) * forward<U>(u);

    }

};

 

class Watts {

public:

    explicit Watts(const int n) : m_n(n) { }

    int get() const { return m_n; }

private:

    int m_n;

};

 

class Seconds {

public:

    explicit Seconds(const int n) : m_n(n) { }

    int get() const { return m_n; }

private:

    int m_n;

};

 

class Joules {

public:

    explicit Joules(const int n) : m_n(n) { }

    int get() const { return m_n; }

private:

    int m_n;

};

 

Joules operator*(const Watts& w, const Seconds& s) {

    return Joules(w.get() * s.get());

}

 

int main() {

    vector<Watts> w;

    w.push_back(Watts(2));

    w.push_back(Watts(3));

    w.push_back(Watts(4));

 

    vector<Seconds> s;

    s.push_back(Seconds(5));

    s.push_back(Seconds(6));

    s.push_back(Seconds(7));

 

    vector<Joules> j;

 

    transform(w.begin(), w.end(), s.begin(), back_inserter(j), Multiplies());

 

    for_each(j.begin(), j.end(), [](const Joules& r) { cout << r.get() << endl; });

}

 

C:\Temp>cl /EHsc /nologo /W4 mult.cpp

mult.cpp

 

C:\Temp>mult

10

18

28

 

You might ask, "is all of this generality really necessary?"  The answer is yes, yes it is.  I've already mentioned how perfect forwarding and decltype make arithmetic operation functors easier to use (by removing the need to repeat element types), more flexible (by dealing with mixed argument and return types), and more efficient (by respecting move semantics).  Essentially, perfect forwarding and decltype allow you to write more "transparent" code.  Inflexible code and inefficient code are not transparent - their presence can't be ignored.

 

 

advanced rules

 

decltype is powered by several rules.  However, if you stick to the pattern above, they don't matter and it just works.  I rarely get to say that about C++, but it's true in this case.

 

Although the vast majority of decltype uses will follow the pattern above, decltype can be used in other contexts.  In that case, you've activated expert mode, and you should read the rules in their entirety.  In the C++0x Working Draft N2857, they're given by 7.1.6.2 [dcl.type.simple]/4.

 

 

but wait, there's more

 

decltype is the fifth and final C++0x Core Language feature being added to VC10.  While it wasn't in the VC10 CTP, it's in VC10 Beta 1.  Also in VC10 Beta 1 are many C++0x Standard Library features, which I'll be blogging about soon!

 

Stephan T. Lavavej

Visual C++ Libraries Developer

Published Wednesday, April 22, 2009 10:06 AM by vcblog

Comments

# decltype: C++0x Features in VC10, Part 3 | Microsoft Share Point

Wednesday, April 22, 2009 8:57 PM by blah

# re: decltype: C++0x Features in VC10, Part 3

Would decltype(Forward<T>(T()) + Forward<U>(U())) work as a preceding return type?

Wednesday, April 22, 2009 10:00 PM by Stephan T. Lavavej [MSFT]

# re: decltype: C++0x Features in VC10, Part 3

No, because T and U might not have default constructors.

Thursday, April 23, 2009 5:16 AM by KristofU

# re: decltype: C++0x Features in VC10, Part 3

Couldn't there be a default for the auto keyword in this case where the return type of the returned expression is used? Instead of having to essentially repeat the code.

Something like this :

template <typename T, typename U>

auto operator()(T&& t, U&& u) const -> decltype(__default__)

{

return forward<T>(t) * forward<U>(u);

   }

Thursday, April 23, 2009 3:00 PM by Stephan T. Lavavej [MSFT]

# re: decltype: C++0x Features in VC10, Part 3

That was considered, but dropped between N1607 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1607.pdf) and N1705 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1705.pdf) due to insufficient support from members of the Evolution Working Group. See N2869 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2869.html) for the whole sequence of papers from decltype's evolution.

Friday, April 24, 2009 7:05 AM by Leo Davidson

# re: decltype: C++0x Features in VC10, Part 3

This is a very long comment so I've split it into four parts: Two question parts, then some thoughts / rambling, and finally a thank-you. :-)

** Null-reference trick **

"Technically, decltype(forward<T>(*static_cast<T *>(0)) + forward<U>(*static_cast<U *>(0))) could go on the left, but that's an abomination."

I agree that is ugly.

When you say you could use that trick to avoid naming particular variables, you mean in that specific example and not in general, right?

As I understand things, the trick doesn't preserve the lvalue/rvalue-ness of the particular variables, which could be important in determining the return type, so it won't always work. (It works in this example because the lvalue/rvalue-ness isn't important, assuming a typical operator*.)

OTOH, if you could always use the trick then you could have something like std::forward which does it and hides the ugliness.

** What if the variable names vary? **

Looking at the examples in this post again makes me wonder, what if the return statement doesn't always use the same variables? For example, what goes in place of "t???" here:

template <typename T>

auto operator()(T&& t1, T&& t2) const

-> decltype(forward<T>(t???) * forward<T>(t???))

{

   if (t1 > t2)

   {

       return forward<T>(t1) * forward<T>(t1);

   }

   else

   {

       return forward<T>(t2) * forward<T>(t2);

   }

}

Presumably, if the code will compile at all, then it doesn't matter which permutation of t1 and t2 you use to fill in the two t???. And if the lvalue/rvalue-ness of t1 and t2 are not the same, and the operator* is such that it matters, then the code won't compile because the two return statements will have different types. (Or the return statements' results will be coerced into whatever type the decltype specifies.)

Am I thinking along the right lines, or completely confused?

** Thoughts **

It's quite odd that two "T&&" in the same context can be different despite being written exactly the same. It's like we have hidden meta-types now. :-) Very useful though, when needed, and something that most people should be able to ignore most of the time.

I don't mind that the equation stating the return type goes on the right. (That's consistent with the lambda stuff and presumably dictated by parsing/syntax issues squeezing these features into the existing language.) And I don't mind that we have to explicitly state the return type/equation. (Fair enough.)

I feel for anyone trying to learn C++ but I would not argue against any of these additions as they solve real problems in a way which makes (non-library) code easier to read and write. Anyone learning C++ needs to realise that the aim is not to hold the entire language in your head. Almost nobody does that. There are some features you should be aware of, but not expert in, where you can re-read the relevant chapter/webpage in the rare cases you need them.

It must be a bit more daunting now than it was before, but that's life. IMO, C++ has always been a language that it takes years to master, and where mastery cannot come from a book or even from mental capacity alone. You have to trip over all of the caveats with your own foot before you truly understand them. It can be horrible for beginners but it's still probably my favourite language.

My main complaints about C++ could never be fixed without breaking existing code. (e.g. I hate the fact that you can get default constructors/operators without explicitly asking for them. Anything where the default may be catastrophically wrong should be opt-in, not opt-out, IMO. But it's too late to change that now.)

** Thanks! **

By the way, I read all three parts of this series over the last two days and I want to say thanks for describing things so well and thinking of examples that strike the right balance between complexity and abstraction*. Only the section on forwarding took me a few reads to grasp and I guess that is inherently complex and difficult to explain. I think you did a great job.

(*That is to say, often when language features are described the examples are so abstract that I have to almost compile them in my head to work out what's being demonstrated. Or the examples are so complex, with so many unimportant side details, that it takes time to dig into the important bits. Your examples, OTOH, tended to be clear just from skimming the code. Nice one!)

Friday, April 24, 2009 11:36 AM by Visual Studio Hacks

# Visual Studio Links #113

My latest in a series of the weekly, or more often, summary of interesting links I come across related to Visual Studio. US ISV Developer Evangelism Team posted some links to money saving offers for ISVs when purchasing or upgrading Visual Studio or MSDN

Friday, April 24, 2009 4:58 PM by Nicol Bolas

# re: decltype: C++0x Features in VC10, Part 3

"What if the variable names vary?"

Since return type of a function cannot vary depending on the input data, then the operator* should resolve to the same regardless of the order that t1 and t2 are multiplied in. So it shouldn't matter.

Friday, April 24, 2009 8:11 PM by Leo Davidson

# re: decltype: C++0x Features in VC10, Part 3

@Nicol Bolas:

"then the operator* should resolve to the same regardless of the order"

The same what, is my question. The two return statements are presumably coerced into the type of the decltype statement, and there's an error if that isn't possible. That's my guess anyway, but I was wondering if the guess is correct.

That is, the return type cannot vary but the type of each return statement can vary, so long as it can be turned into the return type.

A bit like this (which compiles) but where the lvalue/rvalue-ness of the types varies rather than the types themselves.

double test(long a, char b)

{

if (a > b)

{

return a * a;

}

else

{

return b * b;

}

}

Friday, April 24, 2009 9:26 PM by Stephan T. Lavavej [MSFT]

# re: decltype: C++0x Features in VC10, Part 3

[Leo Davidson]

> When you say you could use that trick to avoid naming particular variables, you mean in that specific example and not in general, right?

It works in general, so trailing return types weren't an absolutely necessary feature. However, programmer sanity is important, which is why trailing return types were added.

> As I understand things, the trick doesn't preserve the lvalue/rvalue-ness of the particular variables

That information is contained within the deduced types T and U, and preserved by forward<T>(stuff) and forward<U>(stuff) regardless of what you give them (but you have to give them something).

Something like arg<T>() could have been developed, taking nothing and returning T&& to a dereferenced null pointer (this function would be lethal to ever call - but decltype does not evaluate its expression). But instead of putting the return type on the left and teaching people to translate forward<T>(t) into arg<T>() there, putting the return type on the right and teaching people "give decltype exactly what you're going to return" is easier.

> (It works in this example because the lvalue/rvalue-ness isn't important, assuming a typical operator*.)

Although lvalueness/rvalueness doesn't generally affect operator+() and operator*(), my functors have been carefully written to respect them, including in the decltype-powered return type.  Saying decltype(t + u) would not respect lvalueness/rvalueness.

> What if the variable names vary?

Then you need to figure out some expression that has the correct type.  The function can only have one return type, after all.

C++0x <type_traits> provides common_type which may be of use here.

In your case, you're taking two T&&, so forward<T>(t1) * forward<T>(t1) and forward<T>(t2) * forward<T>(t2) are guaranteed to have the exact same type (same inputs, same output).  Note that taking two T&& doesn't produce perfect forwarding, and will probably trigger template argument deduction failure in many cases (where the deduced Ts differ).

> It's quite odd that two "T&&" in the same context can be different despite being written exactly the same.

I'm not sure what you're referring to.

In my examples, I use operator()(T&& t, U&& u).  This allows the arguments to have different types.

A different issue is that in one instantiation, T&& might actually be string&&, while in another instantiation, it might be int&.  This is covered in Part 2, reference collapsing, and is otherwise not fundamentally different from how in C++98/03, X& might actually be vector<int>& in one instantiation and const list<int>& in other (where X is deduced to be const list<int>).

> I feel for anyone trying to learn C++ but I would not argue

> against any of these additions as they solve real problems

> in a way which makes (non-library) code easier to read and write.

Excellently put.

Also, many C++0x features are simpler than the workarounds you'd have to use in C++98/03 (if they were possible at all).  Which can make C++0x easier to learn from scratch (whereas we C++98/03 programmers had to learn the old ways, and now we have to learn the new ways on top of that).

> By the way, I read all three parts of this series over the last two

> days and I want to say thanks for describing things so well and

> thinking of examples that strike the right balance between complexity and abstraction*.

You're welcome! Yes, coming up with simple but plausible examples is tricky.

Tuesday, April 28, 2009 9:23 AM by someone

# re: decltype: C++0x Features in VC10, Part 3

Will VS C++ 2010 deliver real SSE3/SSE4 support (compiler optimization /arch:SSE3 ) instead of just intrinsics?

Wednesday, April 29, 2009 1:34 PM by Gabest

# re: decltype: C++0x Features in VC10, Part 3

Ok, I don't get one thing, after all these post, who can be so obsessed with c++ to come up with all these?

The reason the language is still alive today ( next to java/c#/php/... which have greater momentum, easier syntax, extendablity, etc.), because the programmer can more or less tell or check in the .asm output how it will compile to assembly and yet he does not have to write it in assembly! Anything that shifts our beloved close-to-the-metal c/c++ towards the mentioned languages don't have much "market value" because that area is already full of competition.

I say just keep improving the code generator, add sse/avx/larrabee support, smarter intrinsic translationm, and of course less ICE :P

Wednesday, April 29, 2009 1:42 PM by Andre Vachon

# re: decltype: C++0x Features in VC10, Part 3

We are working on all of that too !

In the coming months, we will share details about the codegen optimizations that will be part of VC10.

BTW, ICEs are quite rare now.  If you find some, please report them back to us!

Wednesday, April 29, 2009 3:59 PM by Stephan T. Lavavej [MSFT]

# re: decltype: C++0x Features in VC10, Part 3

Gabest:

Please note that these features follow C++'s spirit of staying close to the metal while providing modern abstractions.

* Lambdas compile into function objects which benefit from inlining. This is superior to library machinery (like bind() and mem_fn()) which is complicated enough to defeat the inliner.

* auto, static_assert, and decltype are purely compile-time features. They impose no overheads on generated code.

* Rvalue references automatically replace unnecessary dynamic memory allocations with instantaneous pointer twiddling. C++0x mostly eliminates C++98's biggest overhead compared to C, its tendency to perform unnecessary copies.

The compiler is divided into a "front-end" (FE) and "back-end" (BE). Summarizing how they work, the FE parses C++ while the BE generates assembly. These C++0x Core Language features are FE features, unrelated to BE features like code generation, intrinsics, and so forth. FE optimizations like rvalue references and BE optimizations like smarter code generation happily coexist with each other. Also, completely different developers work on the FE and BE, so you don't have to worry that time spent implementing lambdas is somehow taking time away from improving code generation, because it's not.

Friday, May 01, 2009 9:57 AM by AlisdairM

# re: decltype: C++0x Features in VC10, Part 3

To answer a comment above: Wouldn't it be nice to deduce the return type for 'simple' functions, in just the same way that lambda is required to?

template< typename T, typename U >

auto plus( T t, U u ) {

 return t + u;

}

As Steven says, this was considered early in the C++0x cycle, and ultimately rejected.  However, it may yet return as part of the last ongoing piece of work on this part of the standard, see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2825.html

Now I don't want to talk up the chances of success at this point!  The paper proposes chaning the auto keyword (in new function declarations) to [] so that functions and lambda have a similar syntax.  Essentially, anything that is 'callable' is introduced by square brackets, and there is no difference between a regular function, and a lambda expression with an empty capture list (meaning you can use lambda for callbacks into many Windows APIs!)

The problem is that many consider this syntax ugly, and we will shortly have two popular compilers shipping with the auto syntax.

So the idea is not dead yet, but will probably be decided one way or the other at the next standards meeting in July.

Friday, May 01, 2009 10:13 AM by longtime c++/Mfc dev

# re: decltype: C++0x Features in VC10, Part 3

I'm eagerly awaiting posts on the compiler back-end improvements :-)

Saturday, May 09, 2009 7:57 AM by Leo Davidson

# re: decltype: C++0x Features in VC10, Part 3

Sorry to have taken so long to reply. Really appreciate your answer, too.

>> It's quite odd that two "T&&" in the same context can be different despite being written exactly the same.

> I'm not sure what you're referring to.

I'll try to explain what I mean. Perhaps I'm just confused about how this stuff works, so anyone else reading please don't assume any of this is correct!

Say you have:

auto operator()(T&& t1, T&& t2) const;

You might call that with two strings, but where the first string is an lvalue and the second string is an rvalue.

In that case "T&& t1" and "T&& t2" have different (meta-)types.

i.e. "T&&" means two different things within the same function body and its meaning can only be resolved in combination with a variable name. I assumed that's why there was no zero-argument thing like arg<T>().

Or would that call simply not compile? (That sounds like what you said in your reply, but I'm not certain we're talking about the same thing.) Do the two T&& arguments have to have the same l/rvalue-ness, and you have to use a new template typename if you want to allow them to be different?

If it wouldn't compile, does that mean there's no way to have a function where the two arguments must be the same type as each other, but may have different l/rvalue-ness?

Either way it seems fine, on the face of it. I'm not criticising the design; just trying to understand exactly how things will work.

Thanks, again, for your time!

Monday, May 11, 2009 3:44 PM by Stephan T. Lavavej [MSFT]

# re: decltype: C++0x Features in VC10, Part 3

[Leo Davidson]

> Say you have:

> auto operator()(T&& t1, T&& t2) const;

This is broken. (You can write it, but it won't be useful.) If you want perfect forwarding, you need to take T1&&, T2&&, T3&&, etc.

> You might call that with two strings, but where the first string is an lvalue and the second string is an rvalue.

> In that case "T&& t1" and "T&& t2" have different (meta-)types.

> i.e. "T&&" means two different things within the same function body and its meaning can only be resolved

> in combination with a variable name.

This is incorrect. C++ (both 98/03 and 0x) doesn't work like this.

In C++98/03, if I have template <typename C> void foo(const C& c1, const C& c2), and I call foo(v, l) with a vector<int> v and list<int> l, will this compile? No, because template argument deduction will fail. When you call foo(v, l), the compiler tries to figure out what C should be. The arguments are telling it that C should be vector<int> and list<int> simultaneously, which is impossible. (I am deliberately omitting other details of template argument deduction and overload resolution here.)

C++0x works identically. Given template <typename T> void bar(T&& t1, T&& t2) and bar(expression1, expression2), either expression1 and expression2 cause the same T to be deduced (in which case compilation succeeds), or they don't (in which case compilation fails).

C++0x adds a couple of twists to template argument deduction (specifically, when matching the function parameter T&& t against the function argument X x where x is an lvalue, T is deduced to be X& instead of plain X), substitution (specifically, reference collapsing, where you start with T&&, substitute X& for T which generates X& &&, which then collapses to X&), and overload resolution, but otherwise everything works like C++98/03. Within a single instantiation, a single type like T&& never means two different things. It can mean a single slightly surprising thing (for example, X&), but only one.

> Or would that call simply not compile?

Bingo. VC actually generates a good compiler error for this:

C:\Temp>type meow.cpp

#include <iostream>

#include <ostream>

#include <string>

using namespace std;

template <typename T> void meow(T&& t1, T&& t2) {

   cout << t1 << endl;

   cout << t2 << endl;

}

string rv() {

   return "kittens";

}

int main() {

   string lv("fluffy");

   meow(lv, rv());

}

C:\Temp>cl /EHsc /nologo /W4 meow.cpp

meow.cpp

meow.cpp(18) : error C2782: 'void meow(T &&,T &&)' : template parameter 'T' is ambiguous

       meow.cpp(6) : see declaration of 'meow'

       could be 'std::string'

       or       'std::string &'

> (That sounds like what you said in your reply, but I'm not certain we're talking about the same thing.)

> Do the two T&& arguments have to have the same l/rvalue-ness, and you have to use a new template typename

> if you want to allow them to be different?

Yes.

> If it wouldn't compile, does that mean there's no way to have a function where the two arguments

> must be the same type as each other, but may have different l/rvalue-ness?

There is a way: static_assert.

C:\Temp>type purr.cpp

#include <iostream>

#include <ostream>

#include <string>

#include <type_traits>

using namespace std;

template <typename T, typename U> void purr(T&& t, U&& u) {

   static_assert(

       is_same<

           typename remove_reference<T>::type,

           typename remove_reference<U>::type

       >::value,

       "purr(t, u) requires t and u to have identical types, "

       "but they can have different lvalueness/rvalueness.");

   cout << t << endl;

   cout << u << endl;

}

string rv() {

   return "kittens";

}

int main() {

   string lv("fluffy");

   purr(lv, rv());

}

C:\Temp>cl /EHsc /nologo /W4 purr.cpp

purr.cpp

C:\Temp>purr

fluffy

kittens

Attempting to call purr(lv, 1729); fails:

C:\Temp>cl /EHsc /nologo /W4 purr.cpp

purr.cpp

purr.cpp(14) : error C2338: purr(t, u) requires t and u to have identical types, but they can have different lvalueness/rvalueness.

       purr.cpp(27) : see reference to function template instantiation 'void purr<std::string&,int>(T,U &&)' being compiled

       with

       [

           T=std::string &,

           U=int

       ]

(I'm not bothering with constness here. You can also use SFINAE to avoid a hard error.)

> Either way it seems fine, on the face of it. I'm not criticising the design; just trying to understand exactly how things will work.

Sorry for the confusion. (I myself was confused when you started talking about a construct, foo(T&&, T&&), which I hadn't presented in my posts.)

I hope things make sense now.

Tuesday, May 12, 2009 6:30 AM by Leo Davidson

# re: decltype: C++0x Features in VC10, Part 3

Huge thanks! That explained things perfectly and has cleared up what I had misunderstood.

I enjoyed the continuation of the feline theme, too. :)

Tuesday, May 12, 2009 3:04 PM by Jusendo

# Beta1?

Hey guys,

So is this going to be in Beta1? and when is that supposed to be released? I was hoping tech-Ed, but  AFAIK, no beta 1 are being haded out.

Tuesday, May 12, 2009 3:54 PM by Stephan T. Lavavej [MSFT]

# re: decltype: C++0x Features in VC10, Part 3

[Jusendo]

> So is this going to be in Beta1?

Yes. VC10 Beta 1 will contain lambdas, auto, static_assert, rvalue references, decltype, and our updated Standard C++ Library implementation.

> and when is that supposed to be released?

Magic 8 Ball says: Better not tell you now.

Thursday, May 21, 2009 12:45 PM by ComponentGear.com Feed

# VC 10: Stephan T. Lavavej and Damien Watkins - Inside STL

Visual Studio 2010 Beta 1 introduces a number of exciting new features for the C++ developer as we include

Friday, May 22, 2009 5:11 AM by Ge yong

# How to make this feature work in beta1?

I installed beta1 & create a native console project to try new features.

auto: works.

decltype, lambda: doesn't work. I copied the code here & let IDE to compile (not from command line), but failed.

Do I foget to do to some configuration?

Friday, May 22, 2009 6:16 AM by Stephan T. Lavavej [MSFT]

# re: decltype: C++0x Features in VC10, Part 3

[Ge yong]

> decltype, lambda: doesn't work. I copied the code

> here & let IDE to compile (not from command line),

> but failed.

"doesn't work" and "failed" are not specific enough for my psychic debugging powers. Can you at least show me the compiler errors?

Friday, May 22, 2009 7:25 AM by Ge yong

# re: decltype: C++0x Features in VC10, Part 3

I find the reason why it seems to "doesn't work".

The code:

#include "stdafx.h"

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

int main() {

   int *p= nullptr;

   vector<int> v;

   for (int i = 0; i < 10; ++i) {

       v.push_back(i);

   }

   for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

   cout << endl;

}

In output window, no problem:

1>f:\temp\tconsole\tconsole\tconsole.cpp(13): error C2065: 'nullptr' : undeclared identifier

unfortunately, what I see first is Error List window, the messages in it are confusing:

Error 1 IntelliSense: identifier "nullptr" is undefined f:\temp\tconsole\tconsole\tconsole.cpp 13 10 TConsole

Error 2 IntelliSense: expected an expression f:\temp\tconsole\tconsole\tconsole.cpp 21 34 TConsole

Error 3 error C2065: 'nullptr' : undeclared identifier f:\temp\tconsole\tconsole\tconsole.cpp 13 1 TConsole

When I clicked on "Error 2", the character '[' is highlighted on line 21 (the line started with for_each). This make me think "lambda doesn't work".

by the way, I see "nullptr" become blue, color of keyword. How to make it work?

Friday, May 22, 2009 6:34 PM by Stephan T. Lavavej [MSFT]

# re: decltype: C++0x Features in VC10, Part 3

Thanks for the clarification.

VC10 Beta 1 doesn't support nullptr, and we currently have no plan to add it to VC10.

VC10 Beta 1 Intellisense doesn't support the C++0x core language features that the actual compiler supports. This will obviously change.

It appears that VC10 Beta 1 Intellisense recognizes some C++0x keywords as such, but that doesn't mean that it recognizes the actual features.

Monday, June 01, 2009 11:22 AM by daniel b

# re: decltype: C++0x Features in VC10, Part 3

Thanks for posting these detailed explanations of new X++0x features.  Very informative and interesting stuff, even if it sometimes hurts my head a bit!

Much of the C++0x features (at least what I call the "sexy" features) seem to be similar to those of C# : auto(var), lambdas, initializer lists, enum class, and Range-for.  I suppose these types of features are so compelling it's hard to ignore them.  This is good news for devs programming with both languages, like myself (practically every time I open VC++ I wish I could use C# foreach, lambdas and enum classes).

Very exciting stuff!

New Comments to this post are disabled
 
Page view tracker