I'm hitting a C2440 error:
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
When I try to compile the following test code with visual studio 10, it seems to be working fine with clang -Weverything and armcc.
Is there a particular reason this isn't working, and is there a workaround aside from "don't do that"?
#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;
}
EDIT: I'm aware that the D<TYPE, int> case is providing an lvalue, and D<TYPE, non-int> case is providing only an rvalue. That is quite intentional.
Ambiguous user-defined-conversion
Ambiguous is the keyword.
D<int, int> test3;
int t3 = test3;
Here the compiler doesn't know wether to use const int &D<int, char>::operator const int &() or int &D<int, int>::operator int &().
The problem is that t3 is int and not int& or const int&.
If you actually work with references everything works fine (or at least as expected):
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
The problem is for the compiler to decide wether to use the const version of the base class or the non-const version of the inheriting class.
I would recommend you overload the const version of the operator in the inheriting class, so that the compiler can choose which one to use from this class only:
#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;
}
Other ways to address this problem:
You can now manually cast to int&
D<int, int> test3;
int t3 = (int&)test3;
or manually cast to const int&
D<int, int> test3;
int t3 = (const int&)test3;
or explicitly call the functions
D<int, int> test3;
int t3 = test3.operator int &();
or
D<int, int> test3;
int t3 = test3.operator const int &();
or you could make both function 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; }
};
Or you could just remove the redifinition of the function (As TYPE doesn't change).
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) {}
};
Yet I guess the latter 2 variants are not what you want because of the const.
There are two function of operator TYPE&():
public D<int, int>::operator int&()
private D<int, char>::operator const int&() const
possible to call it explicit:
int t3 = test3.::D<int, int>::operator int&();
concerning gcc is allow to call (if puplic inheritance):
int t3 = test3.::D<int, char>::operator const int&();
but for msvc isn't
gcc allow both variant and seems it is right:
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 allow only second variant.
msvc does not considers that test3 is not const and does not select const and non const function.
Seems it is msvc bug.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With