当前位置: 首页 > 知识库问答 >
问题:

MSVC:带有模板化转换运算符和多重继承的Bug

卢才艺
2023-03-14

下面的代码不是用MSVC编译的。它与gcc,clang和icc一起编译很好。我猜是个虫子,对吧?

你有/知道一些解决办法吗?

#include <type_traits>

struct A
{
    template <
        typename C
        ,typename = std::enable_if_t<std::is_same_v<C, int>>
    >
    operator C() const{
        return 12;
    }
};

struct B
{
    template <
        typename C
        , typename = std::enable_if_t<std::is_same_v<C, char>>
        , typename F = int
    >
    operator C() const
    {
        return 'A';
    }
};

struct AB : A, B
{
};

int main(){
    AB ab;
    int i = ab;
    char c = ab;
}

错误文本为:

example.cpp

<source>(34): error C2440: 'initializing': cannot convert from 'AB' to 'char'

<source>(34): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

Compiler returned: 2

我已经向微软发布了一个错误报告。

在godbolt上看到

共有1个答案

车思淼
2023-03-14

这似乎真的是MSVC中的一个bug。最后一个基在算子模板推导过程中似乎没有被考虑。为了前任。

struct AB : A, B // -> only A's templated operator considered
struct AB  : B, A // -> only B's templated operator considered

在您的示例中,您可以删除模板运算符并直接使用类型(Live)(在这种情况下使用模板没有太大意义):

#include <type_traits>

struct A
{
    operator int() const{ return 12;}
};

struct B
{
    operator char() const { return 'A'; }
};

struct AB : A, B
{
};

int main(){
    AB ab;
    int i = ab;
    char c = ab;
}

或者您可以移到类模板,例如(Live):

#include <type_traits>

template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
struct A
{
   
    operator T() const{
        return 12;
    }
};

template <typename T, typename = std::enable_if_t<std::is_same_v<T,char>>>
struct B
{
    operator T() const
    {
        return 'A';
    }
};

struct AB : A<int>, B<char>
{
};

int main(){
    AB ab;
    int i = ab;
    char c = ab;
}

或者可以在单个类中重载模板化操作数(Live):

struct A
{
    template <
        typename C
        , typename = std::enable_if_t<std::is_same_v<C, int>>
    >
        operator C() const {
        return 12;
    }

    template <
        typename C
        , typename = std::enable_if_t<std::is_same_v<C, char>>
        , typename F = int
    >
        operator C() const
    {
        return 'A';
    }
};


struct AB : A
{
};

int main() {
    AB ab;
    int i = ab;
    char c = ab;
}
 类似资料:
  • 首先,考虑这个C代码: 根据标准的预期,这将无法编译,因为< code>print在每个基类中都被单独考虑,以便进行重载解析,因此调用是不明确的。这是Clang (4.0)、gcc (6.3)和MSVC (17.0)的情况——见godbolt结果。 现在考虑以下片段,其唯一的区别是我们使用而不是: 我希望结果与前一种情况相同,但事实并非如此 - 虽然gcc仍然抱怨,但Clang和MSVC可以编译这

  • Jinja 最为强大的地方在于他的模板继承功能,模板继承允许你创建一个基础的骨架模板, 这个模板包含您网站的通用元素,并且定义子模板可以重载的 blocks 。 听起来虽然复杂,但是其实非常初级。理解概念的最好方法就是通过例子。 基础模板 在这个叫做 layout.html 的模板中定义了一个简单的 HTML 文档骨架,你可以 将这个骨架用作一个简单的双栏页面。而子模板负责填充空白的 block:

  • 模板继承可以减少页面内容的重复定义,实现页面内容的重用 典型应用:网站的头部、尾部是一样的,这些内容可以定义在父模板中,子模板不需要重复定义 block标签:在父模板中预留区域,在子模板中填充 extends继承:继承,写在模板文件的第一行 定义父模板base.html { % block block_name % } 这里可以定义默认值 如果不定义默认值,则表示空字符串 { %

  • Jade 支持通过 block 和 extends 关键字来实现模板继承。 一个块就是一个Jade的"block" ,它将在子模板中实现,同时是支持递归的。 Jade 块如果没有内容,Jade会添加默认内容,下面的代码默认会输出block scripts, block content, 和 block foot. html head h1 My Site - #{title}

  • 继承 继承能够创建可复用的模板,定义页面的骨架,然后被子模板填充,子模板又可以作为父模板被继承。 继承主要通过两个标签语法实现 <t:template> 定义要继承的父模板 <b:block> 在父模板中用来定义可以被填充的区域;在子模板中用来定义将内容填充到父模板的指定区域 定义父模板 layout.vdt <div> <b:header> <div>父模板头部</div> </

  • 模板继承是 ThinkCMF推荐一种布局方式,它比上一篇讲的模板布局更灵活;模板继承就是你先定义一个基础的模板,在这个基础模板你可以设置很多个区块( block),然后在其它实际要渲染的子模板文件中用 extend标签继承这个基础模板,在子模板中定义name相同的 block,这样就可以对基础模板中定义的区块进行重载; 每个区块都是<block></block>这样的标签,如: <block na

  • 模板继承是 ThinkCMF推荐一种布局方式,它比上一篇讲的模板布局更灵活;模板继承就是你先定义一个基础的模板,在这个基础模板你可以设置很多个区块( block),然后在其它实际要渲染的子模板文件中用 extend标签继承这个基础模板,在子模板中定义name相同的 block,这样就可以对基础模板中定义的区块进行重载; 每个区块都是<block></block>这样的标签,如: <block na

  • 模板继承是一项更加灵活的模板布局方式,模板继承不同于模板布局,甚至来说,应该在模板布局的上层。模板继承其实并不难理解,就好比类的继承一样,模板也可以定义一个基础模板(或者是布局),并且其中定义相关的区块(block),然后继承(extend)该基础模板的子模板中就可以对基础模板中定义的区块进行重载。 因此,模板继承的优势其实是设计基础模板中的区块(block)和子模板中替换这些区块。 每个区块由{