【问题标题】:Template user defined conversions to abstract class reference and the Intel compiler模板用户定义转换为抽象类引用和英特尔编译器
【发布时间】:2014-03-27 12:29:09
【问题描述】:

我有以下(非常简化的)“容器”类:

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;
};

它应该以共享指针的形式将对象存储在boost::any 容器中。如果我在容器中存储一些 boost::shared_ptr&lt;some_type&gt; 类型的对象,我想通过用户定义的转换来获取引用 (const some_type&amp;),这将允许执行以下操作:

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>();

有时,我需要存储从某个抽象类型派生的对象,并对该抽象类型的引用进行相同类型的类型转换,例如,像这样:

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>();

用户定义的转换和模板“getter”都适用于 GCC。但是,英特尔 C++ 编译器在“getter”工作时似乎存在(用户定义的)转换问题。

例如,以下代码适用于 GCC,但不适用于 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);
}

使用 Intel,我收到此错误:

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)

另一方面,如果我在main()foo() 中将base 替换为derived(或使用“getter”而不是foo() 中的类型转换),英特尔一切正常也。当T是抽象类时,是否可以说服Intel编译器使用用户定义的类型转换为引用类型?

提前感谢您的任何想法。


编辑:有趣的是,使用类型转换为指针类型可以正常工作。如果我添加

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

container 类并将foo() 替换为

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

那么它也适用于英特尔。

【问题讨论】:

  • 我对 **boost::any_cast&lt; boost::shared_ptr&lt;T&gt; &gt;(&amp;m_content) 而不是 *boost::any_cast&lt; boost::shared_ptr&lt;T&gt; &gt;(m_content) 的推理感到困惑(或者为什么 m_content 不只是一个 boost::shared_ptr&lt;T&gt; &gt; 开始,但我猜测失去了原因因为那是由于您的简化);从源头来看,any_cast 似乎确实支持 any 对象的传递引用(请参阅boost.any.hpp)。
  • @JAB 实际上any_cast 有两种变体,一种采用(常量/非常量)对any 的引用并返回T(因此创建一个临时),另一种它接受一个指向any 的指针并返回一个指向T 的指针(随后可用于从函数返回对T 的引用,因为它不会创建T 的临时实例)。
  • 在您提到的情况下,T 在技术上是shared_ptr&lt;T&gt;,并且由于您立即取消引用共享指针,因此除了尝试保存之外,您没有理由需要创建额外的指针一点点空间/CPU周期。 (大多数编译器无论如何都会忽略副本,因为shared_ptr&lt;T&gt; 确实是在那个位置使用的临时对象,不是吗?除非您发现您使用的编译器在这种情况下不执行省略,使用指针form 似乎是一种不必要的抢先优化。)
  • @JAB 我明白了,它实际上也有效。谢谢。编辑了问题。
  • gcc.godbolt.org 上闲逛表明ICC will perform 转换为非常量参考。牺牲一点 const 正确性,你可能会有一个可行的解决方法。

标签: c++ icc


【解决方案1】:

我会在 getter 中返回一个指针:

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

这避免了转换问题,并且如果您将空指针传递给您的容器,也不会立即崩溃。

例子:

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

您还可以添加一个函数valid() 来检查容器中是否有东西:

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

编辑:您对问题的补充完全遵循这个方向。

【讨论】:

  • 感谢您的评论。这就是为什么我提到该类“非常简化”:-) 然而,我在这里不明白的是,这就是我的问题的(可能有些隐藏)点,为什么英特尔编译器没有“看到”用户定义当T 是抽象类型时类型转换为引用类型,而转换为指针类型(抽象/非抽象)则不是这种情况。由于我想避免任何类型的非标准构造,问题可能是我做错了什么还是英特尔编译器中存在错误。
  • 我在您的代码中没有看到错误。但是,有时并非每个编译器都支持符合标准的构造...
【解决方案2】:

好的,看来这是英特尔C++编译器的一个bug,已经在bug跟踪列表中归档了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-08
    • 2017-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-07
    • 1970-01-01
    相关资源
    最近更新 更多