Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Help with template function overloading

1 view
Skip to first unread message

marco.g...@gmail.com

unread,
Sep 19, 2010, 2:08:28 PM9/19/10
to
Dear all,

I need to write a function, say "do_something", which differs in the
number and type of input (template) parameters.
Specifically, I'd like to call "do_something" in these ways:
* do_something(v) // 1 template argument
* do_something<1>(m) // 1 compile-time arg and 1 template argument
* do_something<tag>(m) // 2 template arguments
where "v" is a vector and "m" is a matrix (so they have different
types), and "tag" is a tag-like class.

At the end of this email there is a toy example which should make you
things more clear (... not so toy since I need to integrate this kind
of functions in Boost.uBLAS).

If I compile this code with GCC 4.4.4,

$ g++ -Wall -Wextra -ansi -pedantic -g -o do_something
do_something.cpp

I get this error:

--- [error] ---
do_something.cpp: In instantiation of ‘vector_traits<row_major_tag>’:
do_something.cpp:101: instantiated from here
do_something.cpp:60: error: no type named ‘value_type’ in ‘struct
row_major_tag’
--- [/error] ---

Could someone point me out where I am wrong and how to fix it?

--- [code] ---
#include <cstddef>
#include <iostream>

template <typename ExprT>
struct expr
{
typedef ExprT expr_type;
};

template <typename ExprT>
struct vector_expr: expr< vector_expr<ExprT> >
{
typedef ExprT expr_type;

expr_type const& operator()() const
{
return *static_cast<expr_type*>(this);
}

expr_type& operator()()
{
return *static_cast<expr_type*>(this);
}
};

template <typename ExprT>
struct matrix_expr: expr< matrix_expr<ExprT> >
{
typedef ExprT expr_type;

expr_type const& operator()() const
{
return *static_cast<expr_type*>(this);
}

expr_type& operator()()
{
return *static_cast<expr_type*>(this);
}
};

template <typename T>
struct vector: vector_expr< vector<T> >
{
typedef T value_type;
};

template <typename T>
struct matrix: matrix_expr< matrix<T> >
{
typedef T value_type;
};

struct row_major_tag {};
struct column_major_tag {};

template <typename V>
struct vector_traits
{
typedef typename V::value_type value_type;
};

template <typename M>
struct matrix_traits
{
typedef typename M::value_type value_type;
};

template <typename ExprT>
typename vector_traits<ExprT>::value_type
do_something(vector_expr<ExprT> const& ve)
{
std::cout << "do_something<ExprT>" << std::endl;
return 0;
}

template <std::size_t Dim, typename ExprT>
typename matrix_traits<ExprT>::value_type
do_something(matrix_expr<ExprT> const& me)
{
std::cout << "do_something<size_t,ExprT>" << std::endl;
return 0;
}

template <typename TagT, typename ExprT>
typename matrix_traits<ExprT>::value_type
do_something(matrix_expr<ExprT> const& me)
{
std::cout << "do_something<TagT,ExprT>" << std::endl;
return 0;
}

int main()
{
typedef double value_type;

vector<value_type> v;
matrix<value_type> m;

value_type res;

res = do_something(v);
res = do_something<1>(m);
res = do_something<row_major_tag>(m);
}
--- [/code] ---

Thank you very much in advance!!

Best,

-- Marco

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Daniel Krügler

unread,
Sep 19, 2010, 8:29:54 PM9/19/10
to
On 19 Sep., 20:08, "marco.guazz...@gmail.com"

<marco.guazz...@gmail.com> wrote:
> I need to write a function, say "do_something", which differs in the
> number and type of input (template) parameters.
> Specifically, I'd like to call "do_something" in these ways:
> * do_something(v) // 1 template argument
> * do_something<1>(m) // 1 compile-time arg and 1 template argument
> * do_something<tag>(m) // 2 template arguments
> where "v" is a vector and "m" is a matrix (so they have different
> types), and "tag" is a tag-like class.
>
> At the end of this email there is a toy example which should make you
> things more clear (... not so toy since I need to integrate this kind
> of functions in Boost.uBLAS).
>
> If I compile this code with GCC 4.4.4,
>
> $ g++ -Wall -Wextra -ansi -pedantic -g -o do_something
> do_something.cpp
>
> I get this error:
>
> --- [error] ---
> do_something.cpp: In instantiation of ‘vector_traits<row_major_tag>’:
> do_something.cpp:101: instantiated from here
> do_something.cpp:60: error: no type named ‘value_type’ in ‘struct
> row_major_tag’
> --- [/error] ---
>
> Could someone point me out where I am wrong and how to fix it?

The problem is, that during overload resolution the
compiler *may* instantiate even those function templates
that are not selected, see [temp.inst]/5:

"If the overload resolution process can determine the correct
function to call without instantiating a class template
definition, it is unspecified whether that instantiation
actually takes place."

In your particular example, the compiler attempts to
instantiate the overload

template <typename ExprT>
typename vector_traits<ExprT>::value_type

do_something(vector_expr<ExprT> const&);

for your last call. During this attempt,
the specialization vector_traits<row_major_tag>
is instantiated because that is the return type
of this function. By doing that the class
member declarations are supposed to be
instantiated which leads to the invalid type
declaration

typedef typename row_major_tag::value_type value_type;

This error does not happen in the immediate
context of the declaration of do_something,
so this won't be silently ignored.

Note that a similar situation was reported
in an earlier thread:

http://preview.tinyurl.com/2vqo4kj

You can sfinae-protect your function declaration
by adding a helper trait, see below:

Replace above vector_traits by the following
version:

template <typename T>
struct has_value_type {
template <typename> struct wrapper {};
template <typename U>
static char test(wrapper<typename U::value_type>*);
template <typename>
static char (&test(...))[2];
static const bool value = sizeof(test<T>(0)) == 1;
};

template <typename V, bool = has_value_type<V>::value>
struct vector_traits {};

template <typename V>
struct vector_traits<V, true>


{
typedef typename V::value_type value_type;
};

> template <typename M>
> struct matrix_traits
> {
> typedef typename M::value_type value_type;
> };

A similar fix should be done for this guy.

HTH & Greetings from Bremen,

Daniel Krügler

Stefan van Kessel

unread,
Sep 20, 2010, 6:46:06 AM9/20/10
to
On 9/19/2010 8:08 PM, marco.g...@gmail.com wrote:

[...]

> --- [error] ---
> do_something.cpp: In instantiation of ‘vector_traits<row_major_tag>’:
> do_something.cpp:101: instantiated from here
> do_something.cpp:60: error: no type named ‘value_type’ in ‘struct
> row_major_tag’
> --- [/error] ---

[...]

> template<typename T>
> struct vector: vector_expr< vector<T> >
> {
> typedef T value_type;
> };
>
> template<typename T>
> struct matrix: matrix_expr< matrix<T> >
> {
> typedef T value_type;
> };
>
> struct row_major_tag {};
> struct column_major_tag {};
>
> template<typename V>
> struct vector_traits
> {
> typedef typename V::value_type value_type;
> };

[...]

> template<typename ExprT>
> typename vector_traits<ExprT>::value_type
> do_something(vector_expr<ExprT> const& ve)
> {
> std::cout<< "do_something<ExprT>"<< std::endl;
> return 0;
> }
>

The problem here is that when you call do_something<row_major_tag>(m), the
compiler tries to get the typedef vector_traits<row_major_tag>::value_type even
though the argument to do_something doesn't fit anyway. Since row_major_tag
doesn't have a nested typedef value_type the typedef in vector_traits fails.
There are numerous ways you can fix or design around that. I'll just offer you
one such example:

#include <boost/mpl/if.hpp>
#include <boost/mpl/has_xxx.hpp>

[...]

BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);
struct dummy{ typedef void value_type; };

template <typename ExprT>
typename std::enable_if<
has_value_type<ExprT>::value,
typename vector_traits<
typename boost::mpl::if_<
has_value_type<ExprT>,
ExprT,
dummy>::type
>::value_type
>::type


do_something(vector_expr<ExprT> const& ve)
{
std::cout << "do_something<ExprT>" << std::endl;
return 0;
}

[...]

Have a nice day,
Stefan van Kessel

Alphonso

unread,
Sep 20, 2010, 6:46:28 AM9/20/10
to
Hi again, Marco,

Let's look at your m:

m is of type: matrix < double > == matrix < T >
==> here T = double

which is of type: matrix_expr < matrix < double > > ==
matrix_expr < ExprT >
==> here ExprT = matrix < double >

which is of type: expr < matrix_expr < matrix < double > > > ==
expr < ExprT >
==> here ExprT = matrix_expr < matrix < double > >

You probably want

res = do_something<row_major_tag>(m);

to instantiate

template <typename TagT, typename ExprT>
typename matrix_traits<ExprT>::value_type
do_something(matrix_expr<ExprT> const& me)
{
std::cout << "do_something<TagT,ExprT>" << std::endl;
return 0;

}

with

TagT = row_major_tag
ExprT = matrix < double >

but the compiler has to do some magic to deduce the second template
argument.
It has to notice the following thing: when ExprT is substituted with
matrix < double >, there is an implicit up-cast of m to a reference
its base type which is required. Much simpler is to try to instantiate
the most promising candidate of do_nothing for which it is not
necessary to deduce any template arguments.

When you comment out this function

template <typename ExprT>
typename vector_traits<ExprT>::value_type
do_something(vector_expr<ExprT> const& ve)
{
std::cout << "do_something<ExprT>" << std::endl;
return 0;

}

and, of course,

res = do_something ( v );

he finds the appropriate version do_something.

When you specify explicitly

res = do_something<row_major_tag, matrix <value_type> >(m);

then he is happy.

To summarize, I think that the compiler tries to make the overload
resolution by instantiating the most appealing template, i. e. the
most straightforward one and when this fails, he complains.

Hope that was useful.

Best regards,
Asen Krastev

Alphonso

unread,
Sep 20, 2010, 6:45:36 AM9/20/10
to
Hi, Marco,

I am not sure what exactly you are trying to do, but this line

res = do_something<row_major_tag>(m);

attempts to instantiate this function

template <typename ExprT>
typename vector_traits<ExprT>::value_type
do_something(vector_expr<ExprT> const& ve)
{
std::cout << "do_something<ExprT>" << std::endl;
return 0;

}

and since the return type should be vector_traits < row_major_tag
>::value_type, which is typedef-ed to be row_major_tag::value_type,
the compiler complains that there is no such type.

I don't know what to say about fixing this problem because I don't
understand the reasoning behind this code. I think you should revise
the goal you are trying to achieve with these templates.


Best regards,
Asen Krastev

marco.g...@gmail.com

unread,
Sep 20, 2010, 6:48:56 PM9/20/10
to
On Sep 20, 12:45 pm, Alphonso <antipatter...@gmail.com> wrote:
> Hi, Marco,
>
> I am not sure what exactly you are trying to do, but this line
>
> res = do_something<row_major_tag>(m);
>
> attempts to instantiate this function
>
> template <typename ExprT>
> typename vector_traits<ExprT>::value_type
> do_something(vector_expr<ExprT> const& ve)
> {
> std::cout << "do_something<ExprT>" << std::endl;
> return 0;
>
> }
>
> and since the return type should be vector_traits < row_major_tag>::value_type, which is typedef-ed to be row_major_tag::value_type,
>
> the compiler complains that there is no such type.
>
> I don't know what to say about fixing this problem because I don't
> understand the reasoning behind this code. I think you should revise
> the goal you are trying to achieve with these templates.
>

Hi Asen,

I try to explain my goal with an example.
Suppose "do_something" is a "size" function:
* size(v): return the size of the given vector
* size< dimension_number >(m): return the size of the dimension_number
dimension of the given matrix.
For instance, with dimension_number == 1, the function returns the
number of rows
* size< layout_tag >(m): return the size of the specified dimension of
the given matrix.
For instance, with layout_tag == leading the function returns the
size of the leading dimension (note, a matrix might have either a row-
major or a column-major layout).

I've just tried the solutions proposed by Daniel and Stefan and...
wow! They work!.
I've tried in the past with SFINAE too, but I've failed.
Now I know why: I used the boost::enabled_if; now I realizes that it
was better to use boost::lazy_enable_if.

Thanks to all!

Best,


-- Marco

0 new messages