Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template user defined conversions to abstract class reference and the Intel compiler

Tags:

c++

icc

I have the following (very simplified) "container" class:

class container
{
    public:

        template<typename T> container(const boost::shared_ptr<T> &rhs)
            : m_content(rhs) { }

        template<typename T>
        operator T const & () const
        {
            return get<T>();
        }

        template<typename T>
        T const & get() const
        {
            return *boost::any_cast< boost::shared_ptr<T> >(m_content);
        }

    private:
        boost::any m_content;
};

It should store objects in the boost::any container in the form of a shared pointer. If I store some object, say, of the boost::shared_ptr<some_type> type in the container, I would like to get the reference (const some_type&) simply by a user-defined conversion which would allow to do something like this:

boost::shared_ptr<some_type> x(new some_type);
container cx = x;

...

// user-defined conversion
const some_type &y = cx;

// a template conversion using a "getter"
const some_type &y = cx.get<some_type>();

Sometimes, I need to store objects derived from some abstract type and do the same sort of type conversion to the reference of this abstract type, for example, like this:

boost::shared_ptr<some_abstract_type> x(new some_derived_type);
container cx = x;

...

// user-defined conversion
const some_abstract_type &y = cx;

// a template conversion using a "getter"
const some_abstract_type &y = cx.get<some_abstract_type>();

Both the user-defined conversion and the template "getter" work fine with GCC. However, the Intel C++ compiler seems to have a problem with the (user-defined) conversion while the "getter" works.

For example, the following code works with GCC but not with Intel:

#include <iostream>
#include <boost/any.hpp>
#include <boost/shared_ptr.hpp>

class container
{
    public:

        template<typename T> container(const boost::shared_ptr<T> &rhs)
            : m_content(rhs) { }

        template<typename T>
        operator T const & () const
        {
            return get<T>();
        }

        template<typename T>
        T const & get() const
        {
            return *boost::any_cast< boost::shared_ptr<T> >(m_content);
        }

    private:
        boost::any m_content;
};

class base
{
    public:
        virtual ~base() { }
        virtual void f() const = 0;
};

class derived : public base
{
    public:
        virtual ~derived() { }
        virtual void f() const { std::cout << "hello\n"; }
};

void foo(const container &c)
{
    const base & a = c;
    a.f();
}

int main()
{
    boost::shared_ptr<base> a(new derived);
    container c = a;
    foo(c);
}

With Intel, I get this error:

test.cpp(44): error: no suitable user-defined conversion from "const container" to "const base" exists
      const base & a = c;
                       ^

compilation aborted for test.cpp (code 2)

On the other hand, if I replace base with derived in both main() and foo() (or use the "getter" instead of the type conversion in foo()), everything works fine with Intel too. Is it possible to convince the Intel compiler to use the user-defined type conversion to the reference type when T is an abstract class?

Thanks in advance for any ideas.


EDIT: Interestingly, using the type conversion to the pointer type works fine. If I add

template<typename T>
operator T const * () const
{
    return &get<T>();
}

to the container class and replace foo() with

void foo(const container &c)
{
    const base * a = c;
    a->f();
}

then it works also with Intel.

like image 724
Algebraic Pavel Avatar asked Mar 27 '14 12:03

Algebraic Pavel


1 Answers

I would return a pointer in the getter:

template<typename T>
T const * get() const {
    return boost::any_cast< boost::shared_ptr<T> >(m_content);
}

This avoids the conversion problem, and does not crash immediately if you pass a null pointer to your container.

Example:

void foo(const container &c)
{
    const base* a = c.get<base>();
    a->f();
}

You could also add a function valid() which checks if there is something in the container:

bool valid() const {
    return m_content != NULL;
}

Edit: Your addition to your question follows exactly in this direction.

like image 106
Danvil Avatar answered Oct 22 '22 14:10

Danvil