Template type deduction of reference
I've been playing around with type deduction/printing using templates with code of the form:
#include <iostream>
template <typename T>
class printType {};
template <typename T>
std::ostream& operator<<(std::ostream& os, const printType<T>&)
{
os << "SomeType"; return os;
}
template <typename T>
std::ostream& operator<<(std::ostream& os, const printType<T*>&)
{
os << printType<T>() << "*"; return os;
}
template <typename T>
std::ostream& operator<<(std::ostream& os, const printType<T&>&)
{
os << printType<T>() << "&"; return os;
}
// etc... and can call on a variable through
template <typename T>
printType<T> print(T) { return printType<T>(); }
int main()
{
int a = 7;
int *p = &a;
int &r = a;
//OK: return SomeType*
std::cout << "type of p: " << print(p) << std::endl;
//Hmmmm: returns SomeType <- (no &: can I get around this?)
std::cout << "type of r: " << print(r) << std::endl;
}
I am wondering whether or not I can get the last line to return int&
, that is, either:
int&
or somehow work out it should return a printType<T&>
when I pass it r; or
(ii)whether this is unavoidable because of the way the variable is passed to the function.
Are there any ways around this by changing the form of print or using some 开发者_如何学Goother template trickery? If solutions exists, I'd prefer non-C++0x, but is always good to see what short cuts will, if not already, be available in the future.
There is no way to work this around. An expression p
, where p
names a reference, always has the type the reference refers to. No expression ever has type T&
. So you cannot detect whether an expression originated from a reference or not.
This cannot be done with C++0x either. It's a deep principle of C++ that there are no expressions that have reference type. You can write decltype(r)
to get the type of what r
names instead of what type the expression r
has. But you will not be able to write print(r)
, unless print
is a macro of course, but I don't see why you would go that horrible road.
I take what I previously said back. I think I may have a way to make this work in pure c/c++, albeit in a very messy way. You would need to pass a pointer into your functions...
i.e. bool hello_world(std::string & my_string, const std::string * const my_string_ptr) {
bool hello_world(std::string my_string, const std::string * const my_string_ptr) {
if you now tested
if ( &my_string == my_string_ptr )
It would evaluate true if the var was passed by reference, and false if passed by value.
Of course doubling your variables in all your functions probably isn't worth it...
Johannes is right... not in pure c++. But you CAN do this. The trick is to cheat. Use an embedded scripting language like perl to search your source. Here's an embedded perl module:
http://perldoc.perl.org/perlembed.html
Pass it the function name, variable name, and source location and then use a regex to find the variable and check its type. Really this might be a better solution for your code in general, assuming you are always going to have a source handy.
I will post a function for this basic approach in a bit... gotta take care of some morning work! :)
Even if you don't want to distribute the source, you could create some sort of packed function/var data file that you could parse through @ runtime and get an equivalent result.
Edit 1
For example... using the # I32 match(SV *string, char *pattern)
function in the Perl Embed tutorial, you could do something like:
bool is_reference(const char * source_loc, const char * function_name,
const char * variable_name) {
std::ifstream my_reader;
char my_string[256];
SV * perl_line_contents;
bool ret_val = false;
char my_pattern [400]=strcat("m/.*",function_name);
my_pattern=strcat(my_pattern, ".*[,\s\t]*");
my_pattern=strcat(my_pattern, variable_name);
my_pattern=strcat(my_pattern, "[\s\t]*[\(,].*$");
my_reader.open(source_loc.c_str());
while (!my_reader.eof()) {
my_reader.getline(my_string,256);
sv_setpv(perl_line_contents,my_string);
if(match(perl_line_contents,my_pattern)) {
ret_val= true;
}
}
return ret_val;
}
... there... two ways to do this (see above update).
You can use SFINAE with integral types or anything you can convert from "0" this way:
template <typename T>
class is_reference
{
struct yes { char a, b; };
typedef char no;
template <typename U> static no test(T y) { }
template <typename U> static yes test(...) { }
public:
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
#include <iostream>
struct not_constructable_from_int {};
struct constructable_from_int { constructable_from_int(int x) { } };
int
main()
{
std::cout << is_reference<int>::value << std::endl;
std::cout << is_reference<int&>::value << std::endl;
std::cout << is_reference<not_constructable_from_int>::value << std::endl;
std::cout << is_reference<not_constructable_from_int&>::value << std::endl;
std::cout << is_reference<constructable_from_int>::value << std::endl;
std::cout << is_reference<constructable_from_int&>::value << std::endl;
}
Note that the test is basically "can you call test<T>(0)
" which you can't if T
is a reference or if T
is just any class you can't convert from 0
. Unfortunately you can always call test<T>(T())
which kind of surprised me (even if T
is int
).
As you can see if you are willing to make your types constructable from int
then the test works on them, which really confuses me given the test<T>(T())
result...
精彩评论