【问题标题】:C2440 error when using typecasting operator overload with Visual Studio 10在 Visual Studio 10 中使用类型转换运算符重载时出现 C2440 错误
【发布时间】:2016-02-16 17:27:18
【问题描述】:

我遇到了 C2440 错误:

C:\code>cl test.cpp /EHsc                       

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86                 
Copyright (C) Microsoft Corporation.  All rights reserved.                                      

test.cpp                                                                                        
test.cpp(18) : error C2440: 'initializing' : cannot convert from 'D<TYPE,SELECTOR>' to 'int'
    with
    [
        TYPE=int,
        SELECTOR=int
    ]
    Ambiguous user-defined-conversion

当我尝试使用 Visual Studio 10 编译以下测试代码时,它似乎与 clang -Weverything 和 armcc 一起工作正常。

这是否有特殊原因不起作用,除了“不要那样做”之外还有其他解决方法吗?

#include <iostream>

template <typename TYPE, typename SELECTOR = int> struct D  {
    TYPE x;
    D() : x(2) {}
    D(TYPE X) : x(X) {}

    operator const TYPE&() const { return x; }
};
template <typename TYPE> struct D<TYPE, int> : public D<TYPE, char> {
    D() : D<TYPE, char>(3) {}
    operator TYPE&() { return D<TYPE, char>::x; }
};

int main() {

    D<int, int> test3;
    int t3 = test3;

    D<int, char> test2;
    int t2 = test2;

    std::cout << +t3 << " " << +t2 << "\n";  // Expected output "3 2" (which we get from clang)
    return 0;
}

编辑:我知道D&lt;TYPE, int&gt; 案例提供了一个左值,而D&lt;TYPE, non-int&gt; 案例只提供了一个右值。这是故意的。

【问题讨论】:

  • 在 gcc 上运行良好
  • 那么派生类中的operator int&amp;() 是否从基类中隐藏了operator const int&amp;(),或者它是一个重载?谁知道呢!
  • 如果签名相同,则它会覆盖但可能与命名空间规范相匹配。这个例子与 const 不同,因此可以调用 const 和非 const 版本。

标签: c++ templates visual-c++ visual-c++-2010


【解决方案1】:

Ambiguous user-defined-conversion

模棱两可是关键字。

D<int, int> test3;
int t3 = test3;

这里编译器不知道是使用const int &amp;D&lt;int, char&gt;::operator const int &amp;()还是int &amp;D&lt;int, int&gt;::operator int &amp;()

问题在于t3int 而不是int&amp;const int&amp;。 如果您实际使用引用,一切正常(或至少按预期):

D<int, int> test3;
int& t31 = test3; //OK
const int& t32 = test3; //OK

D<int, char> test2;
int& t21 = test2; //Doesn't work
const int& t22 = test2; //OK

问题在于编译器决定是使用基类的 const 版本还是继承类的非 const 版本。

我建议您在继承类中重载 const 版本的运算符,以便编译器只能从该类中选择使用哪一个:

#include <iostream>

template <typename TYPE, typename SELECTOR = int> struct D {
    TYPE x;
    D() : x(2) {}
    D(TYPE X) : x(X) {}

    operator const TYPE&() const { return x; }
};
template <typename TYPE> struct D<TYPE, int> : public D<TYPE, char> {
    D() : D<TYPE, char>(3) {}

    operator const TYPE&() const { return D<TYPE, char>::x; }
    operator TYPE&() { return D<TYPE, char>::x; }
};

int main() {

    D<int, int> test3;
    int t3 = test3; //OK
    int& t31 = test3; //OK
    const int& t32 = test3; //OK

    D<int, char> test2;
    int t2 = test2; //OK
    //int& t21 = test2; //Doesn't work
    const int& t22 = test2; //OK

    std::cout << +t3 << " " << +t2 << "\n";  // Expected output "3 2" (which we get from clang)
    return 0;
}

解决此问题的其他方法:

您现在可以手动转换为int&amp;

D<int, int> test3;
int t3 = (int&)test3;

或手动转换为const int&amp;

D<int, int> test3;
int t3 = (const int&)test3;

或显式调用函数

D<int, int> test3;
int t3 = test3.operator int &();

D<int, int> test3;
int t3 = test3.operator const int &();

或者你可以同时使用const

template <typename TYPE> struct D<TYPE, int> : public D<TYPE, char> {
    D() : D<TYPE, char>(3) {}
    operator const TYPE&() const { return D<TYPE, char>::x; }
};

或者您可以删除函数的重新定义(因为 TYPE 不会改变)。

template <typename TYPE, typename SELECTOR = int> struct D {
    TYPE x;
    D() : x(2) {}
    D(TYPE X) : x(X) {}

    operator const TYPE&() const { return x; }
};

template <typename TYPE> struct D<TYPE, int> : public D<TYPE, char> {
    D() : D<TYPE, char>(3) {}
};

但我猜后两个变体不是您想要的,因为 const

【讨论】:

  • 两个版本的重载的只读与读写方面是相当有意的。实际情况是一个轻量级的类,其中“查找”函数比简单的D&lt;TYPE, char&gt;::x 复杂得多,左值情况下有几十个运算符重载。
  • @Simon Kraemer:以及如何/为什么将operator const TYPE&amp;() const 添加到派生类解决歧义?在重载解决方案的歧义方面,原始变体和更新变体有什么区别?
  • @jkerian 因为您的非常量运算符重载隐藏了基类中的 const 运算符。编译器现在必须决定您是要使用派生类的版本还是隐式转换为基类并使用基类的运算符。当您对非引用变量进行复制赋值时,它是模棱两可的。
  • @jkerian 你可能还想看看这个问题的答案:stackoverflow.com/questions/4152799/…
  • @Simon Kraemer:只是指出......如果 msvs 在您的链接答案中实现了该机制,则不会有歧义,这不会是错误。 (正如我假设我尝试过的其他三个编译器一样)
【解决方案2】:

operator TYPE&amp;()有两个功能:

public D<int, int>::operator int&() 
private D<int, char>::operator const int&() const

可以明确地称呼它:

int t3 = test3.::D<int, int>::operator int&();

关于 gcc 是否允许调用(如果是公共继承):

int t3 = test3.::D<int, char>::operator const int&();

但对于 msvc 不是

gcc 允许这两种变体,似乎是正确的:

D<int, int> test3;
const D<int, int> test4;

int t3 = test3; // called D<int, int>::operator int&();
int t4 = test4; // called D<int, char>::operator const int&() const;

msvc 只允许第二种变体。

msvc 不认为 test3 不是 const 也不选择 const 和非 const 函数。

似乎是 msvc 错误。

【讨论】:

  • 我的第一版代码是私有继承,所以它是私有的。
  • 原来 public/private 根本没有改变错误,但我在发布之前尝试过改变它。
猜你喜欢
  • 1970-01-01
  • 2021-01-08
  • 1970-01-01
  • 1970-01-01
  • 2013-11-21
  • 1970-01-01
  • 2015-12-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多