Can C++ compiler try different (template T) implementations until it finds one that compiles (for T)?
// 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?
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.
As mentioned, these are ambiguous. You should look into boosts enable_if
or similar, combined with e.g. is_function
.
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;-).
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).
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
精彩评论