【问题标题】:MSVC: Bug with templated conversion operators and multiple inheritanceMSVC:模板转换运算符和多重继承的错误
【发布时间】:2020-12-10 20:42:32
【问题描述】:

我有以下代码不能用 MSVC 编译。它可以与 gcc、clang 和 icc 一起编译。应该是bug吧?

您有/知道一些解决方法吗?

#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

我已经向 Microsoft 发布了 bug report

godbolt这里查看

【问题讨论】:

  • 对于我们这些手头没有 MSVC 的人来说,如果您显示发生的错误会很有帮助。
  • 你是对的,但以防万一你不知道,godbolt 现在也有 MSVC 编译器了。
  • 我认为您对enable_if 的使用不正确。您不应该限制默认值(请参阅链接中标记为“错误”的示例。但似乎仍然存在 MSVC 错误。
  • 这就是为什么 B 有一个额外的模板参数(F)实际上没有使用的原因......
  • 我真的很喜欢 cppreference 并且也阅读了那篇文章。您可以在他们的示例下发现,将非类型模板参数推导出为相同类型(如 int)时存在 ABI 问题。因此,我更喜欢在 SFINAE 检查中使用默认类型参数,因为它们会在早期导致硬错误。但是使用该解决方案,您必须添加一些虚拟类型...

标签: c++ visual-c++ c++17 c++20 cl


【解决方案1】:

这似乎确实是 MSVC 中的一个错误。最后一个碱基在算子模板推演过程中似乎没有考虑。例如。

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

【讨论】:

  • 感谢您的回答。不幸的是,我的真实案例并没有那么简单的 SFINAE 检查...
【解决方案2】:

我找到了workaround

在这里你可以看到我的解决方案。我更改了继承层次结构并引入了转发转换运算符:

#include <type_traits>
#include <utility>

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

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

    template <
        typename C
        , typename Base = A
        , typename = std::enable_if_t<
            std::is_convertible_v<Base, C>
            &&
            !std::is_same_v<C, char>
        >
    >
    operator C() const
    {
        return static_cast<C>(*static_cast<const Base*>(this));
    }
};

struct AB : B
{
};

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

【讨论】:

    猜你喜欢
    • 2020-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-27
    相关资源
    最近更新 更多