views:

174

answers:

6

Suppose I'm writing a template function foo that has type parameter T. It gets an object of type T that must have method bar(). And inside foo I want to create a vector of objects of type returned by bar.

In GNU C++ I can write something like that:

template<typename T>
void foo(T x) {
    std::vector<__typeof(x.bar())> v;
    v.push_back(x.bar());
    v.push_back(x.bar());
    v.push_back(x.bar());
    std::cout << v.size() << std::endl;
}

How to do the same thing in Microsoft Visual C++? Is there some way to write this code that works in both GNU C++ and Visual C++?

+9  A: 

You can do that in standard c++

template<typename T>
struct id { typedef T type; };

template<typename T>
id<T> make_id(T) { return id<T>(); }

struct any_type {
  template<typename T>
  operator id<T>() const { return id<T>(); }
};

template<typename T, typename U>
void doit(id<T>, U& x) {
  std::vector<T> v;
  v.push_back(x.bar());
  v.push_back(x.bar());
  v.push_back(x.bar());
  std::cout << v.size() << std::endl;
}

template<typename T>
void foo(T x) {
    doit(true ? any_type() : make_id(x.bar()), x);
}

See Conditional Love for an explanation.

Johannes Schaub - litb
Now, that's just wrong. Yeah, I know; it's "correct". But it's still WRONG. I think I need to go lie down for a minute.
Marcelo Cantos
@Marcelo, conditional love has proven useful on other occasions too. See [this one](http://stackoverflow.com/questions/1353757/how-do-i-refer-to-stdsinconst-valarraydouble)
Johannes Schaub - litb
My head hurts in some kind of good way... It's a spooky thing alright! If I recall correctly, isn't C++0x supposed to come up with a goody for us here ?
Matthieu M.
@Matthieu, another guy mentions `decltype`. I think that is the C++0x way of getting the type `x.bar()` returns.
Johannes Schaub - litb
Thanks! That's also good, although looks scary. But typedef-s are enough for me.
rem
@rem, if it's possible to add that typedef to the classes (you have access to them and can modify them), you should at any rate use that typedef, anyway.
Johannes Schaub - litb
@Johannes, I _was_ being ironic (just in case you got the wrong impression). It's actually one of the cleverest C++ tricks I've seen.
Marcelo Cantos
@Marcelo, no worries :)
Johannes Schaub - litb
+4  A: 

If you're requiring the type used in your template to have the "bar" function, you can also require it to have a typedef for the type returned from bar. That's the way the standard library usually handles this type of issue (every container has a value_type typedef, for example).

class Bar1 {
public:
    typedef int bar_type;

    bar_type bar();
    ...
};

template<typename T>
void foo(T x) {
    std::vector<T::bar_type> v;
    v.push_back(x.bar());
    v.push_back(x.bar());
    v.push_back(x.bar());
    std::cout << v.size() << std::endl;
}

Bar1 b;
foo(b);
SoapBox
You should use `typename T::bar_type` for the code to be compliant with the standard.
Matthieu M.
Thanks! I'll use this solution. I am new to generic programming and didn't think about typedef-s.
rem
A: 

You could try Boost.Typeof, which claims to support VC 8.

#include <boost/typeof/typeof.hpp>

template<typename T>
void foo(T x) {
    std::vector<BOOST_TYPEOF(x.bar())> v;
    ...
KennyTM
Thanks! But I'd prefer not to create dependencies on additional libaries.
rem
A: 

If you're using Visual C++ 10, they support the "decltype" operator, which will return the type of the given expression.

DeadMG
This is true for any C++ compiler that supports the C++0x feature decltype; GCC 4.3 or newer supports this as well.
Patrick Johnmeyer
+1  A: 

If you defer the work to another template function, you can use template argument deduction on T::bar to figure it out.

// templated on the original type, and the return type of the function
template <typename T, typename mem_fn_return_type>
void doThePush (T& instance, mem_fn_return_type (T::*barptr)(void))
{
  std::vector<mem_fn_return_type> v;
  v.push_back((instance.*barptr)());
  v.push_back((instance.*barptr)());
  v.push_back((instance.*barptr)());
  std::cout << v.size() << std::endl;
}

template <typename T>
void foo(T x)
{
  doThePush(x, &T::bar);
}

However, if you need the type in several places, it is probably better to use the techniques in one of the other answers.

Patrick Johnmeyer
Thanks! Yes, I need to use the type in multiple places.
rem
+3  A: 

C++0x provides the decltype keyword as part of the standard, which solves your problem like so:

template<typename T>
void foo(T x) {
    std::vector<decltype(x.bar())> v;
    v.push_back(x.bar());
    v.push_back(x.bar());
    v.push_back(x.bar());
    std::cout << v.size() << std::endl;
}

Visual Studio 2010 also supports this, as do GCC 4.3+ and Comeau 4.3.9+ (thanks Patrick).

AshleysBrain
as does GCC 4.3+ and Comeau 4.3.9+
Patrick Johnmeyer
Thanks! I need to study new C++0x features some day :) But I need the code to compile in older versions either. BTW, I'm getting compilation error trying to compile it in gcc-3.4.3: http://ideone.com/3jWob
rem
and if bar possibly returns a reference you have to use `typename remove_reference<decltype(x.bar())>::type`
sellibitze