I'm trying to build a small C++ example using boost fusion. However, Visual Studio 2013 gives me build errors for the following piece of code. It should simply go over a associative struct and print all member names to the console:
#include <iostream>
#include <type_traits>
#include <boost/fusion/adapted/struct/define_assoc_struct.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/algorithm/transformation/zip.hpp>
#include <boost/fusion/algorithm/transformation/transform.hpp>
namespace keys
{
struct name
{};
struct id
{};
}
BOOST_FUSION_DEFINE_ASSOC_STRUCT((), Student,
(std::string, name, keys::name)
(int, id, keys::id)
);
struct getnames
{
template<typename Sig>
struct result;
template <typename S, typename T>
struct result<getnames(S, T)>
{
typedef std::string type;
};
template<class Struct, class N>
typename result<getnames(Struct, N)>::type operator() (const N& i) const
{
return boost::fusion::extension::struct_member_name<Struct, i>::call();
}
};
struct print
{
template<typename Sig>
struct result;
template <typename T>
struct result<print(T)>
{
typedef void type;
};
template<class S>
void operator() (const S& i) const
{
std::cout << i << std::endl;
};
};
int main()
{
Student j = {"John", 42};
auto names = boost::fusion::transform(j, getnames());
boost::fusion::for_each(names, print());
return 0;
}
This is my error:
boost/fusion/view/transform_view/detail/deref_impl.hpp(38): error C2039: 'type' : is not a member of 'boost::mpl::apply<boost::fusion::detail::apply_transform_result<getnames>,const std::basic_string<char,std::char_traits<char>,std::allocator<char>> &,boost::mpl::na,boost::mpl::na,boost::mpl::na,boost::mpl::na>'
and four more errors which are coming up because of the first one.
To be honest, I am not an expert in the usage of boost fusion so maybe I simply missed something important here and someone else can help me.
There are several problems with your code.
1) In the Functor getnames, the signature of the result type and the signature of operator() are inconsistent (one takes one argument, the other takes two).
2) in the operator()(const N& i), i is a runtime variable. It cannot appear as a template parameter in the expression boost::fusion::extension::struct_member_name<Struct, i>.
I am not sure how to help without knowing what you want to do with getnames. Try to get to a consistent code first.
Related
I am trying to use the boost::variant with template types. For example, I have a template type Tag<T> and the boost::variant AnyTag comprises types such as Tag<double>, Tag<int> and Tag<std::string>. Each Tag<T> has members of type T.
Now, I would like to put those variants in a container and simply assign values during runtime, e.g.,
for(AnyTag & tag: AllTags) {
setValue(tag, getValueFromXml());
}
The function setValue(AnyTag &tag, T &val) must use the runtime type of the AnyTag tag in order to correctly assign the tag with the correct value.
My attempt to solving the problem can be found below and it makes use of another variant which included only the possible T types that could be used in the AnyTag (TagValueType).
template<typename T, typename = void>
class Tag {};
template <typename T>
class Tag<T, EnableIf<std::is_arithmetic<T>>> {
public:
T value = 0;
std::string address = "";
T maxValue = std::numeric_limits<T>::max();
typedef T value_type;
};
template <typename T>
class Tag<T, DisableIf<std::is_arithmetic<T>>> {
public:
T value;
std::string address = "";
typedef T value_type;
};
typedef boost::variant<Tag<std::string>,
Tag<double>,
Tag<int>,
> AnyTag;
typedef boost::variant<std::string, double, int> TagValueType;
class tag_set_value_visitor: public boost::static_visitor<void>
{
const TagValueType & value;
public:
tag_set_value_visitor(const TagValueType & val): value(val){}
template <typename T>
void operator()(T & tag) const
{
tag.value = boost::get<typename T::value_type>(value);
}
};
inline void setValue(AnyTag & tag, const TagValueType & val) {
assert(tag.which() == val.which());
boost::apply_visitor( tag_set_value_visitor(val), tag );
}
Unfortunately, this approach is not what I would like because for example during compilation there is not problem if I do the following:
AnyTag a = Tag<int>();
setValue(a, double(1.3));
but during runtime, the boost library detects the type mismatch and crashes the program.
So, my solution is kind of a type erasure that just postpones the problem.
What I would like to have is a setValue(AnyTag &tag, T &val) where T is the runtime type of the AnyTag.
I get that that's what the variant's visitor tries to do, but there is a problem in this case because when we construct the visitor we must know the type that we are going to use.
Any ideas or any thoughts about this problem?
P.S.: Sorry for the long post but I couldn't find a way to explain my thought process with fewer words.
Use¹ a binary visitor.
Implement the operator() to do nothing except for corresponding types.
Handle mismatches to taste (I return a boolean indicating success):
Live On Coliru
#include <boost/any.hpp>
#include <boost/variant.hpp>
#include <boost/mpl/vector.hpp>
#include <string>
using namespace boost;
template <typename T>
struct Tag {
T value;
};
using Types = mpl::vector<std::string, double, int>;
using Tags = mpl::transform<Types, Tag<mpl::_1> >::type;
using Variant = make_variant_over<Types>::type;
using AnyTag = make_variant_over<Tags>::type;
namespace mydetail {
struct assign_to : boost::static_visitor<bool> {
template <typename V> bool operator()(Tag<V>& tagged, V const& value) const {
tagged.value = value;
return true;
}
template <typename T, typename V> bool operator()(T&&, V&&) const {
return false;
}
};
}
bool setValue(AnyTag &tag, Variant const& val) {
return boost::apply_visitor(mydetail::assign_to(), tag, val);
}
int main() {
AnyTag t;
t = Tag<std::string>();
// corresponding type assigns and returns true:
assert(setValue(t, "yes works"));
// mismatch: no effect and returns false:
assert(!setValue(t, 42));
assert(!setValue(t, 3.1415926));
}
¹ If I understood your goal correctly. I've focused on the "What I would like to have is a setValue(AnyTag &tag, T &val) where T is the runtime type of the AnyTag." part of the request.
I have a bunch of types that have a name. (They have more features, but for the sake of this discussion only the name is relevant.) These types and their names are setup at compile-time using a macro:
#define DEFINE_FOO(Foo_) \
struct Foo_ : public foo_base<Foo_> { \
static char const* name() {return #Foo_;} \
}
The types are then combined in compile-time lists (classic simple recursive compile-time lists), from which I need to create the list's name by concatenating the names of its objects:
template<class Foo, class Tail = nil>
struct foo_list {
static std::string name_list() {return Foo::name() + "-" + Tail::name();}
};
template<class Foo>
struct foo_list<Foo,nil> {
static std::string name_list() {return Foo::name();}
};
The code is boiled down here to the point where it might contain errors, but in practice this works pretty well.
Except that it creates and then copies around rather long strings at runtime which represent types that actually are well-known at compile-time. Since this is a rather performance-sensitive piece of code that runs on embedded devices, I'd like to change this so that
the list's string is ideally created at compile-time, or, if there's no way to do that, once at runtime, and
I only need to copy around a pointer to a C string, since, according to #1, the strings are fixed in memory.
This compiles with C++03, which we're stuck with right now.
How can I do this?
(In case this enlarges the arsenal of dirty tricks employable for this: The names of the foo objects are only ever created and read by code, and only the foo_list name strings are expected to be human-readable.)
You probably want to look at boost's mpl::string. Example to follow once my coffee has kicked in...
EDIT: So coffee has kicked in... :)
#include <iostream>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/string.hpp>
#include <boost/mpl/vector.hpp>
namespace mpl = boost::mpl;
struct foo
{
typedef mpl::string<'foo'> name;
};
struct bar
{
typedef mpl::string<'bar'> name;
};
struct gah
{
typedef mpl::string<'gah'> name;
};
namespace aux
{
template <typename string_type, typename It, typename End>
struct name_concat
{
typedef typename mpl::insert_range<string_type, typename mpl::end<string_type>::type, typename mpl::deref<It>::type::name>::type base;
typedef typename aux::name_concat<base, typename mpl::next<It>::type, End>::name name;
};
template <typename string_type, typename End>
struct name_concat<string_type, End, End>
{
typedef string_type name;
};
}
template <typename ...Types>
struct type_list
{
typedef mpl::string<> base;
typedef mpl::vector<Types...> type_seq;
typedef typename aux::name_concat<base, typename mpl::begin<type_seq>::type, typename mpl::end<type_seq>::type>::name name;
};
int main(void)
{
typedef typename type_list<foo, bar, gah>::name tlist_name;
std::cout << mpl::c_str<tlist_name>::value << std::endl;
}
I'm sure you are more than competent enough to tweak the above for your situation. NOTE: you will have to ignore the multi-character constant warnings...
Couple of more caveats: the multi-character constant passed to mpl::string cannot be more than 4 characters, so, some how it has to be chunked (or constructed of individual characters), so a long string could be, mpl::string<'this', ' is ', 'a lo', 'ng s', 'trin', 'g'> If this is cannot be done, then the above will not work.. :/
I came up with following solution:
Type is generated as:
const char foo_str [] = "foo";
struct X
{
static const char *name() { return foo_str; }
enum{ name_size = sizeof(foo_str) };
};
Keypoint here is that we know length of its name at compile time. That allow us to calculate total length of names in typelist:
template<typename list>
struct sum_size
{
enum
{
value = list::head::name_size - 1 +
sum_size<typename list::tail>::value
};
};
template<>
struct sum_size<nil>
{
enum { value = 0 };
};
Knowing total length at compile time, we can allocate static buffer of appropriate size for concatenation of strings - so there will be no any dynamic allocations:
static char result[sum_size<list>::value + 1];
That buffer should be filled at runtime, but only once, and that operation is pretty cheap (much faster than previous solution with dynamic allocation of strings and their concatenation in recursion):
template<typename list>
const char *concate_names()
{
static char result[sum_size<list>::value + 1];
static bool calculated = false;
if(!calculated)
{
fill_string<list>::call(result);
calculated = true;
}
return result;
}
Here is full code:
Live Demo on Coliru
#include <algorithm>
#include <iostream>
using namespace std;
/****************************************************/
#define TYPE(X) \
const char X ## _str [] = #X; \
struct X \
{ \
static const char *name() { return X ## _str; } \
enum{ name_size = sizeof(X ## _str) }; \
}; \
/**/
/****************************************************/
struct nil {};
template<typename Head, typename Tail = nil>
struct List
{
typedef Head head;
typedef Tail tail;
};
/****************************************************/
template<typename list>
struct sum_size
{
enum { value = list::head::name_size - 1 + sum_size<typename list::tail>::value };
};
template<>
struct sum_size<nil>
{
enum { value = 0 };
};
/****************************************************/
template<typename list>
struct fill_string
{
static void call(char *out)
{
typedef typename list::head current;
const char *first = current::name();
fill_string<typename list::tail>::call
(
copy(first, first + current::name_size - 1, out)
);
}
};
template<>
struct fill_string<nil>
{
static void call(char *out)
{
*out = 0;
}
};
/****************************************************/
template<typename list>
const char *concate_names()
{
static char result[sum_size<list>::value + 1];
static bool calculated = false;
if(!calculated)
{
fill_string<list>::call(result);
calculated = true;
}
return result;
}
/****************************************************/
TYPE(foo)
TYPE(bar)
TYPE(baz)
typedef List<foo, List<bar, List<baz> > > foo_list;
int main()
{
cout << concate_names<foo_list>() << endl;
}
Output is:
foobarbaz
P.S. How do you use concatenated string? Maybe we don't need to generate concatenated string at all, reducing data space requirement.
For example if you just need to print string - then
template<typename list>
void print();
will be sufficient. But downside is that while decreasing data size - that could lead to increased size of code.
You could make the string static and you only have to construct the string once at runtime and only when it is needed.
Then you return a const reference of them so that there wouldn't be any unnecessary copying.
Example:
template<class Foo, class Tail = nil>
struct foo_list {
static const std::string& name_list() {
static std::string names = Foo::name() + std::string("-") + Tail::name();
return names;
}
};
template<class Foo>
struct foo_list<Foo,nil> {
static const std::string& name_list() {
static std::string names = Foo::name();
return names;
}
};
Might not be the exact code that you'll write, but I think that gives you the point. Also, you could return a const char* by doing names.c_str().
You can consider using an external build step instead of an in-language solution. For example, you could write a tool based on Clang to parse the relevant files and create the T::name implementations automatically in another TU. Then integrate it into your build script.
If we could assume that your only requirement is to actually stream the names of the classes - meaning you don't need the concatenated strings at other places as a whole - you could simply defer streaming but still benefit from meta-programming (as Evgeny already pointed out).
While this solution doesn't satisfy your requirement #1 (one concatenated string), I'd still like to point out a solution for other readers.
Instead of going through a compile-time list of types, the idea is to build a sequence of addresses from all T::name() functions and pass that into a streaming function when needed. This is possible because variables with external linkage can be used as template non-type arguments. Your mileage may vary in terms of data and code size of course, but unless you are in a high-performance environment I expect this approach to be at least equally suitable as no additional strings have to be created at runtime.
Note that I deliberately used variadic templates (not available in C++03) because it's much more readable and simpler to reason about.
"Fiddle" available here.
#include <ostream>
#include <boost/core/ref.hpp>
#include <boost/bind.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/for_each.hpp>
namespace mpl = boost::mpl;
template<typename>
class foo_base
{};
#define DECLARE_FOO(Foo_) \
struct Foo_ : public foo_base<Foo_> { \
static char const* name() {return #Foo_;} \
};
// our own integral constant because mpl::integral_c would have to be specialized anyway
template<typename T, T Value>
struct simple_integral_c
{
operator T() const { return Value; }
};
template<typename T, T ...Values>
struct ic_tuple : mpl::vector<simple_integral_c<T, Values>...>
{};
typedef const char*(*NameFunction)();
template <NameFunction ...Functions>
struct function_list : ic_tuple<NameFunction, Functions...>
{};
template <typename ...Types>
struct function_of_list : function_list<&Types::name...>
{};
struct print_type
{
void operator ()(std::ostream& os, NameFunction name)
{
if (nth++)
os << "-";
os << name();
}
print_type(): nth(0) {}
private:
int nth;
};
// streaming function
template<NameFunction ...Functions>
std::ostream& operator <<(std::ostream& os, function_list<Functions...>)
{
mpl::for_each<function_list<Functions...>>(
boost::bind<void>(print_type(), boost::ref(os), _1)
);
return os;
}
These days with C++14 one would probably write the solution with a powerful library like hana, see this hana fiddle.
I have a
typedef std::tuple<A, B> TupleType;
and would like to use the list of classes
for a "template".
Suppose I have:
template<typename... args>
std::tuple<args...> parse(std::istream &stream) {
return std::make_tuple(args(stream)...);
}
and that I can successfully use it with:
auto my_tuple = parse<A, B>(ifs);
is it possible to avoid having to specify the class list A,B if I already have a
typedef std::tuple<A,B> TupleType;
where the list A,B is already present?
an example:
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
#include <iostream> // std::cerr
#include <fstream> // std::ifstream
#include <tuple> // std::tuple
class A {
public:
A(std::istream &); // May throw FooBaarException
};
class B {
public:
B(std::istream &); // May throw FooBaarException
};
template<typename... args>
std::tuple<args...> parse(std::istream &stream) {
return std::make_tuple(args(stream)...);
}
int main() {
std::ifstream ifs;
ifs.exceptions(ifstream::eofbit | ifstream::failbit | ifstream::badbit);
int res = EXIT_FAILURE;
try {
ifs.open("/some/file/path", std::ios::in | std::ios::binary);
auto my_tuple = parse<A, B>(ifs); // my_tuple is of the type std::tuple<A,B>
/* Here do something interesting with my_tuple */
res = EXIT_SUCCESS;
} catch (ifstream::failure e) {
std::cerr << "error: opening or reading file failed\n";
} catch (FooBaarException e) {
std::cerr << "error: parsing in a constructor failed\n";
}
return res;
}
The underlying problem in your situation seems to be that you'd like to specialize the function template parse for the special case when the template argument is a std::tuple. Unfortunately, this kind of specialization isn't possible with function templates.
However, it is possible with class templates.
So, as a first step, you could define parse as a static function of a struct, like this:
using std::istream;
using std::tuple;
using std::make_tuple;
struct A { A(const istream &) {} };
struct B { B(const istream &) {} };
template <typename... Args>
struct parser
{
/* Your original function, now inside a struct.
I'm using direct tuple construction and an
initializer list to circumvent the order-of-
construction problem mentioned in the comment
to your question. */
static tuple<Args...> parse(const istream &strm)
{ return tuple<Args...> {Args(strm)...}; }
};
template <typename... Args>
struct parser<tuple<Args...>>
{
/* Specialized for tuple. */
static tuple<Args...> parse(const istream &strm)
{ return parser<Args...>::parse(strm); }
};
You can then call it in the desired way:
int main()
{
typedef tuple<A,B> tuple_type;
auto tup = parser<tuple_type>::parse(std::cin);
return 0;
}
As a second step, you can define a function template (again) which passes the arguments on to the right specialization of the struct:
template <typename... Args>
auto parse(const istream &strm) -> decltype(parser<Args...>::parse(strm))
{ return parser<Args...>::parse(strm); }
And now you can use it in exactly the way you wanted:
int main()
{
typedef tuple<A,B> tuple_type;
auto tup = parse<tuple_type>(std::cin);
return 0;
}
(And you can still use it in the old way, too: auto tup = parse<A,B>(std::cin).)
Remark. As mentioned in the comment to parser::parse(), I used direct tuple construction instead of make_tuple to avoid problems with the order of construction of the tuple elements. This is not directly related to your question, but a good thing to do. See how to avoid undefined execution order for the constructors when using std::make_tuple.
There is a standard idiom for this kind of thing. [1]
// Define the "shape" of the template
template<typename Tuple> struct TupleMap;
// Specialize it for std::tuple
template<typename...T> struct TupleMap<std::tuple<T...>> {
using type = std::tuple<T...>; // not necessary but saves typing
// ... inside here, you have access to the parameter pac
}
Here's an example of using it, which might or might not fit your expectations (your example doesn't really indicate your expected use, since it lacks the typedef you promise in your question): liveworkspace.org.
Since litb raised the point, it is possible to force the tuple-components to be constructed in left-to-right order, illustrating another interesting idiom: comb inheritance. See lws.
(Since lws might disappear again, who knows, I'll paste the code here as well):
#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>
// Define the "shape" of the template
template<typename Tuple> struct TupleMap;
// Specialize it for std::tuple
template<typename...T> struct TupleMap<std::tuple<T...>> {
using type = std::tuple<T...>; // not necessary but saves typing
type value;
template<typename Arg>
TupleMap(Arg&& arg)
: value(T(std::forward<Arg>(arg))...) {
}
operator type() { return value; }
};
//Try it out:
using std::get; // Note 2
using Numbers = std::tuple<char, double, int>;
// Note 3
std::ostream& operator<<(std::ostream& out, const Numbers& n) {
return out << get<0>(n) << ' ' << get<1>(n) << ' ' << get<2>(n);
}
int main() {
std::cout << TupleMap<Numbers>(93.14159);
return 0;
}
[1] At least, I think it's a standard idiom. I use it a lot, and think of it as the "can-opener" pattern.
[2] This is needed (or at least, it's my style) to allow the use of get with tuple-like templates defined outside of std. Doing it this way allows ADL to find the appropriate definition of get without forcing me to add specializations to std::get. In this way, it's similar to the standard ADL idiom for begin and end.
[3] You can search SO for a cool hack to specialize operator<< for all tuples. There's a simpler one which can be used for specific tuples, but that's all off-topic for this question, so I just did something easy and dependency free. Note that this works because of the conversion operator in TupleMap
The basic approach is to create a sequence of indices 0, ..., std::tuple_size<Tuple>::value - 1 as a parameter pack Indices and call your function with parse<typename std::tuple_element<Tuple, Indices>::type...>(stream). You'd probably encapsulate the logic into a function parse_tuple<Tuple>(stream) (and some function this one delegates to) which in the end delegates to parse<...>(stream).
First, here is a class template and a function to create a sequence of indices based on the size of a std::tuple. The indices are needed to obtain a list of type from std::tuple:
template <int... Indices> struct indices;
template <>
struct indices<-1> { // for an empty std::tuple<> there is no entry
typedef indices<> type;
};
template <int... Indices>
struct indices<0, Indices...> { // stop the recursion when 0 is reached
typedef indices<0, Indices...> type;
};
template <int Index, int... Indices>
struct indices<Index, Indices...> { // recursively build a sequence of indices
typedef typename indices<Index - 1, Index, Indices...>::type type;
};
template <typename T>
typename indices<std::tuple_size<T>::value - 1>::type const*
make_indices() {
return 0;
}
With this in place, it is quite easy to extract the sequence of types from a std::tuple<T...>:
template<typename T, int... Indices>
T parse_tuple(std::istream &stream, indices<Indices...> const*) {
return parse<typename std::tuple_element<Indices, T>::type...>(stream);
}
template <typename T>
T parse_tuple(std::istream& stream) {
return parse_tuple<T>(stream, make_indices<T>());
}
I've been investigating the use of some judicious static assertions to improve error messages. Here's an example:
#include <type_traits>
template<typename T> struct is_less_than_comparable {
template<typename Test> static char test(decltype(*static_cast<Test*>(nullptr) < *static_cast<Test*>(nullptr)));
template<typename Test> static int test(...);
static const bool value = std::is_same<char, decltype(test<T>(true))>::value;
};
template<typename K, typename V> class map {
public:
static_assert(is_less_than_comparable<K>::value, "Key type must be less-than comparable!");
};
struct x {};
int main() {
map<x, int> intmap;
}
IDEONE will happily reject this code with the nice, clean error message I was hoping to get (once you replace nullptr with 0, anyway). But MSVC will not fire the static assertion and compiles this code just fine- even if I add some member functions and start calling on them.
The problem is with VC++ 2010 handling of the metafunction is_less_than_comparable, not with static_assert.
If you change the code to:
static const bool value = std::is_same<double, decltype(test<T>(true))>::value;
Then it will be false regardless of which overload is picked. The assertion then fires.
Evidently the wrong overload is being selected, SFINAE isn't removing the candidate with char return type.
Simpler test (incorrectly prints 1):
#include <type_traits>
template<typename T> struct is_less_than_comparable {
template<typename Test> static char test(decltype(*static_cast<Test*>(nullptr) < *static_cast<Test*>(nullptr)) b);
template<typename Test> static int test(...);
static const bool value = std::is_same<char, decltype(test<T>(true))>::value;
};
struct x {};
#include <iostream>
int main() {
std::cout << is_less_than_comparable<x>::value << std::endl;
}
The compiler is, for no apparent reason, providing a bool operator<(x, x) operator, thus generating int is_less_than_comparable<T>::test<T>(bool). If a user-defined comparison operator is provided, its return type is correctly picked up. This operator isn't coming in with one of the headers, I can repro the decltype resolving to bool with no headers included.
This generates the proper error:
decltype(*(x*)nullptr < *(x*)nullptr) b;
error C2676: binary '<' : 'x' does not define this operator or a conversion to a type acceptable to the predefined operator
I think this is related to VC++ not performing two-phase lookup of template dependent arguments.
I'm not certain what you're looking for in an answer here, so here's a type trait that works fine in VC++ 2010:
#include <type_traits>
namespace supports
{
namespace details
{
struct return_t { };
template<typename T>
static T& make();
}
template<typename T>
details::return_t operator <(T const&, T const&);
template<typename T>
struct less_than : std::integral_constant<
bool,
!std::is_same<
decltype(details::make<T>() < details::make<T>()),
details::return_t
>::value
> { };
}
What type signature would I need to use if I'd like to determine the type returned by an array (T)'s subscript operator using boost? Note that the arrays for which I would be using this do not contain typedefs and are third-party.
Example. I want to determine that:
SomeArray<int> tmp(1);
int& somevalue = tmp[0]; //would equate
typename subscript_result<SomeArray<int> >::type somevalue = tmp[0];
Something like
template<class T>
struct subscript_result
{
typedef boost::result_of<T::operator[](typename T::difference_type)>::type type;
};
? I've always had trouble with operator[] in type signatures. :|
Thank you!
Perhaps you could use BOOST_TYPEOF / BOOST_TYPEOF_TPL: http://www.boost.org/doc/libs/1_35_0/doc/html/typeof/refe.html#typeof.typo
BOOST_TYPEOF(tmp[0]) i;
In C++0x, you should be able to use decltype(tmp[0]) i;
In answer to the comment. Perhaps you can trick it to not remove const and references with something like that:
#include <boost/typeof/typeof.hpp>
template <class T>
struct identity
{
typedef T type;
};
template <class T>
struct subscript_result
{
template <class Result, class Obj, class Arg>
static identity<Result> get_subscript_type(Result (Obj::*)(Arg));
typedef BOOST_TYPEOF(get_subscript_type(&T::operator[])) aux;
typedef typename aux::type type;
};
#include <vector>
#include <iostream>
template <class Container>
void foo(Container& c)
{
typename subscript_result<Container>::type t = c[0];
++t;
}
int main()
{
//prove that foo gets a reference to vector<int>
std::vector<int> vec(1);
foo(vec);
std::cout << vec[0] << '\n';
}
You'll probably also need to come up with something for const overloads, as well as throw in specializations for arrays / pointers.