views:

127

answers:

5
// First try this:

template <class T> T Read(istream& in) {
  T t;
  in >> t;
  return t;
}

// If there is no operator>>(istream&, T) try this: 

template <class T> T Read(istream& in) {
  return T (in);
}

// If there is no constructor T(istream&) try this:

template <class T> T Read(istream& in) {
  return T::OfStream (in);
}

// now fail.

Can this be implemented?

If not, what are the alternatives?

A: 

In a standard-compliant C++ implementation, these multiple ambiguous templates should produce an error (ambiguity is defined by signature, not by body). I do not know of any C++ compiler which violates the standard to the extent of allowing this code (that doesn't mean there aren't any crazy-enough compilers, just that I haven't heard of any that are;-).

Alex Martelli
+1  A: 

As mentioned, these are ambiguous. You should look into boosts enable_if or similar, combined with e.g. is_function.

Georg Fritzsche
A: 

This cannot be implemented directly as you specified, but there is a workaround. It is possible to define a template conversion operator, and its type parameter can be deduced from the expected target type. Thus, you can introduce a proxy class:

class read_proxy
{
public:
    read_proxy(std::istream& in) : in(in) {}
    template<class T> operator T () { T x; in >> x; return x; }
private:
    std::istream& in;
};

read_proxy read(std::istream& in)
{
    return read_proxy(in);
}

And then use it as you originally wanted:

void foo(float) {}

int main()
{
    int x = read(std::cin);
    foo(read(std::cin)); // float
}

The potential problem here is if someone tries to persist the returned proxy itself, he can run into problems with lifetime (since it holds a simple reference to a stream, and the latter can potentially be destructed before the proxy is).

Pavel Minaev
+8  A: 

Are you familiar with the concept of SFINAE? Using this concept, you can include or exclude function templates from the candidate set based on any property of the template arguments. As Alex Martelli said, however, you have to cause this happen in the signature, not the body of the method.

This means you need to be able to make compile-time decisions concerning some property of type T, and use the result of that decision to force the template signature to become illegal, which will exclude that template from the compiler's candidate set without raising a compilation error.

Boost has two libraries that can facilitate this: Boost.TypeTraits, which allows you to ask things like "is T an array?" or "is T a pointer?" or "is T a subclass of U?" at compile time. The result of that query can be used by Boost.EnableIf to exclude a function (or not, as desired).

You may be able to achieve what you are after using a combination of those libraries. If you are using a specific compiler, you may also be able to achieve similar results using compiler-specific extensions (if that's okay with you). For example, using MSVC, you might be able to make use of the __if_exists keyword. Depending on how closely your simple example mirrors what you really want to do, one method may be cleaner than the other.

Josh Petrie
So how to do it?Ie. How to check whether a class has named constructor OfStream ?
Łukasz Lew
In the above duplicate question, Nicola Bonelli provides a good example. You can replace the use of the compiler-specific typeof() operator there with Boost's BOOST_TYPEOF() macro to increase portability.
Josh Petrie
A: 

As the other answers indicate it is not standard compliant.

You did not show intended usage so it's a bit difficult to figure out what exactly you are after but you can implement the code yourself (see below), which can be improved more with SFINAE to avoid creating transformation templates for each specific class:

#include <iostream>
#include <sstream>

using namespace std;

struct A
{
    int x;
    A() : x(0) {}
};

istream& operator>>(istream& in, A& a)
{
    in >> a.x;
    return in;
}

ostream& operator<<(ostream& on, A& a) { return on << "A: " << a.x; }


struct B
{
    int x;
    B(istream& in) : x(0) { in >> x; }
};

ostream& operator<<(ostream& on, B& b) { return on << "B: " << b.x; }


struct C
{
    int x;
    C() : x(0) {}

    static C OfStreamX(istream& in)
    {
        C c;
        in >> c.x;
        return c;
    }
};

ostream& operator<<(ostream& on, C& c) { return on << "C: " << c.x; }


template <typename T> T Read(istream& in);
template <> A Read(istream& in)
{
    A a;
    in >> a;
    return a;
}

template <> B Read(istream& in) { return B(in); }
template <> C Read(istream& in) { return C::OfStreamX(in); }

int main()
{
    string data("23 45 67");
    istringstream in(data);

    A a = Read<A>(in);
    cout << a << endl;

    B b = Read<B>(in);
    cout << b << endl;

    C c = Read<C>(in);
    cout << c << endl;
}

Output:

A: 23
B: 45
C: 67
stefanB