template <typename T>
class v3 {
private:
T _a[3];
public:
T & operator [] (unsigned int i) { return _a[i]; }
const T & operator [] (unsigned int i) const { return _a[i]; }
operator T * () { return _a; }
operator const T * () const { return _a; }
v3() {
_a[0] = 0; // works
_a[1] = 0;
_a[2] = 0;
}
v3(const v3<T> & v) {
_a[0] = v[0]; // Error 1 error C2666: 'v3<T>::operator []' : 2 overloads have similar conversions
_a[1] = v[1]; // Error 2 error C2666: 'v3<T>::operator []' : 2 overloads have similar conversions
_a[2] = v[2]; // Error 3 error C2666: 'v3<T>::operator []' : 2 overloads have similar conversions
}
};
int main(int argc, char ** argv)
{
v3<float> v1;
v3<float> v2(v1);
return 0;
}
If you read the rest of the error message (in the output window), it becomes a bit clearer:
1> could be 'const float &v3<T>::operator [](unsigned int) const'
1> with
1> [
1> T=float
1> ]
1> or 'built-in C++ operator[(const float *, int)'
1> while trying to match the argument list '(const v3<T>, int)'
1> with
1> [
1> T=float
1> ]
The compiler can't decide whether to use your overloaded operator[] or the built-in operator[] on the const T* that it can obtain by the following conversion function:
operator const T * () const { return _a; }
Both of the following are potentially valid interpretations of the offending lines:
v.operator float*()[0]
v.operator[](0)
You can remove the ambiguity by explicitly casting the integer indices to be unsigned so that no conversion is needed:
_a[0] = v[static_cast<unsigned int>(0)];
or by changing your overloaded operator[]s to take an int instead of an unsigned int, or by removing the operator T*() const (and probably the non-const version too, for completeness).
In simple terms: the compiler doesn't know whether to convert v to const float* and then use the operator[] for a pointer, or to convert 0 to unsigned int and then use the operator[] for const v3.
Fix is probably to remove the operator[]. I can't think of anything it gives you that the conversion operator to T* doesn't already. If you were planning to put some bounds-checking in operator[], then I'd say replace the conversion operators with getPointer functions (since in general you don't want to implicitly convert a safe thing to an unsafe thing), or do what std::vector does, which is that users get a pointer with &v[0].
Another change that lets it compile, is to change operator[] to take an int parameter instead of unsigned int. Then in your code, the compiler unambiguously chooses the interpretation with no conversion. According to my compiler, there is still no ambiguity even when using an unsigned index. Which is nice.
When you the compiler compiles the following
v[0]
it has to consider two possible interpretations
v.operator T*()[0] // built-in []
v.operator[](0) // overloaded []
Neither candidate is better than the other because each one requires a conversion. The first variant requires a user-defined conversion from v3<T> to T*. The second variant requires a standard conversion from int (0 is int) to unsigned int, since your overloaded [] requires an unsigned int argument. This makes these candidates uncomparable (neither is clearly better by C++ rules) and thus makes the call ambuguous.
If you invoke the operator as
v[0U]
the ambiguity will disappear (since 0U is already an unsigned int) and your overloaded [] will be selected. Alternatively, you can declare your overloaded [] with int argument. Or you can remove the conversion operator entirely. Or do something else to remove the ambiguity - you decide.
It is your type conversion operator that is the culprit. v transformed to a float pointer. Now there are two operator []s possible, one is the built in subscript operator for float and the other being the one you defined on v, which one should the language pick, so it is an ambiguity according to ISO.
Remember a class is a friend of itself:
v3(const v3<T> & v)
{
_a[0] = v._a[0];
_a[1] = v._a[1];
_a[2] = v._a[2];
}
When copy something of the same type you are already exposed to the implementation details. Thus it is not a problem to access the implementation directly if that is appropriate. So from the constructor you can access the object you are copying directly and see its member '_a'.
If you want to know the original problem:
The literal '1' in the context 'v[1]' is an integer (this is a synonym of signed integer). Thus to use the operator[] the compiler technically is required to insert a conversion from int to unisgned. The other alternative is to use the operator*() to get a pointer to the internal object and then use the [] operator on the pointer. Compiler is not allowed to make this choice and error out:
Compiler options:
_a[1] = v[1];
// Options 1:
_a[1] = v.operator[]((unsigned int)1);
// Options 2:
_a[1] = v.operator*()[1];
To make it unabigious you can use an unsigned literal;
_a[1] = v[1u];
In the long run it may be worth making this easier for the user.
Convert the operator[] to use int rather than unsigned int then you will get exact matches when integer literals (or you can have two sets of operator[]. One that uses int and one that uses unsigned int).
I didn't see it untill James McNellis posted the full error message, but the ambiguity is not between the two v3::operator[]() functions as it appears to be.
Instead, since there is no exact match between argument types, the compiler can't decide whether to:
a) Use v3::operator[](unsigned int) const, thereby converting the int argument to unsigned, or
b) Use the v3::operator const T*() const conversion followed by the built-in array indexing operator.
You can avoid this by making the operator[] arguments int's rather than unsigned ints. But a better solution would be to avoid an implicit conversion to T* and instead provide a Data() function that did that explicitly.
I had this same issue: I resolved it simply making the typecast operator explicit.
The const version doesn't modify anything. The non-const version allows you to assign things using array notation (v[3] = 0.5;).
Related
The following code compiles under Clang/GCC under the C++17 standard, but does not compile under MSVC with -std:C++17 /Zc:ternary.
struct CStringPtr
{
const char *m_pString = nullptr;
CStringPtr() = default;
CStringPtr( const char *pString ) : m_pString( pString ) { }
operator const char *() const { return m_pString; }
};
int main( int argc, char ** )
{
bool b = !!argc;
const char *X = b ? CStringPtr( "inside" ) : "naked";
const char *Y = b ? "naked" : CStringPtr( "inside" );
CStringPtr Z = b ? CStringPtr( "inside" ) : "naked";
CStringPtr W = b ? "naked" : CStringPtr( "inside" );
// Silence unused-variable warnings.
return X && Y && Z && W;
}
Link to godbolt's compiler explorer with all three: https://godbolt.org/z/6d5Mrjnd7
MSVC emits an error for each of those four lines:
<source>(19): error C2445: result type of conditional expression is ambiguous: types 'const char [6]' and 'CStringPtr' can be converted to multiple common types
<source>(19): note: could be 'const char *'
<source>(19): note: or 'CStringPtr'
Whereas Clang/GCC each call the CStringPtr constructor for the naked string for all four of those cases.
In the MSVC /Zc:ternary documentation they claim that the flag enables standards-compliant resolution of the ternary operator, which implies that either there's a bug in MSVC's implementation or Clang/GCC are not standards-compliant here.
The one other note here is that the MSVC docs mention an exception in this exact case with respect to the const char * type being used:
An important exception to this common pattern is when the type of the operands is one of the null-terminated string types, such as const char*, const char16_t*, and so on. You can also reproduce the effect with array types and the pointer types they decay to. The behavior when the actual second or third operand to ?: is a string literal of corresponding type depends on the language standard used. C++17 has changed semantics for this case from C++14.
So, is MSVC non-compliant under C++17 rules? Or are Clang/GCC?
This is Core issue 1805, which changed ?: to decay arrays and functions to pointers before testing whether the other operand can be converted to it. The example in that issue is basically the one in your question:
struct S {
S(const char *s);
operator const char *();
};
S s;
const char *f(bool b) {
return b ? s : ""; // #1
}
One might expect that the expression in #1 would be ambiguous, since
S can be converted both to and from const char*. However, the
target type for the conversion of s is const char[1], not const char*, so that conversion fails and the result of the
conditional-expression has type S.
It appears that neither GCC nor Clang implements that issue resolution, so they are still testing whether CStringPtr can be converted to an array.
If you manually decay the string literals with a unary +, everyone rejects the example as ambiguous.
Suppose you have a set of pointers (yeah...) :
std::set<SomeType*> myTypeContainer;
Then suppose that you want to search this set from a const method of SomeType:
bool SomeType::IsContainered() const
{
return myTypeContainer.find(this) != myTypeContainer.end();
}
This doesn't work. The this ptr in the method is a const SomeType *const, which I can't put into the find. The issue being that find takes a const-ref, which in this case would mean that the passed pointer is treated as const, but not the thing it points to.
Is there a way to resolve this smoothly (without changing the set template type)?
In order to enable "mixed" comparison in an ordered container, you can use a key_compare type that declares the typename key_compare::is_transparent.
The default comparison functor class of set is std::less<Key>. It is not "transparent". But std::less<void> is "transparent" and performs the comparison of any arguments a and b as long as a<b is well formed. So you could define your own comparison functor type or you could use std::less<void> (or equivalently std::less<>):
set<SomeType*,std::less<>> myTypeContainer;
As you said, in the const member function this becomes const SomeType * (i.e. pointer to const), it can't be implicitly converted to SomeType * (i.e. pointer to non-const), which is the expected parameter type of find.
You could use const_cast to perform explicit conversion.
bool SomeType::IsContainered() const
{
return myTypeContainer.find(const_cast<SomeType *>(this)) != myTypeContainer.end();
}
It would be safe if the cast result is not used for modifying; while std::set::find won't do that.
I was trying to understand the usage of explicit keyword in c++ and looked at this question on SO
What does the explicit keyword mean in C++?
However, examples listed there (actually both top two answers) are not very clear regarding the usage. For example,
// classes example
#include <iostream>
using namespace std;
class String {
public:
explicit String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
};
String::String(int n)
{
cout<<"Entered int section";
}
String::String(const char *p)
{
cout<<"Entered char section";
}
int main () {
String mystring('x');
return 0;
}
Now I have declared String constructor as explicit, but lets say if I dont list it as explicit, if I call the constructor as,
String mystring('x');
OR
String mystring = 'x';
In both cases, I will enter the int section. Until and unless I specify the type of value, it defaults to int. Even if i get more specific with arguments, like declare one as int, other as double, and not use explicit with constructor name and call it this way
String mystring(2.5);
Or this way
String mystring = 2.5;
It will always default to the constructor with double as argument. So, I am having hard time understanding the real usage of explicit. Can you provide me an example where not using explicit will be a real failure?
explicit is intended to prevent implicit conversions. Anytime you use something like String(foo);, that's an explicit conversion, so using explicit won't change whether it succeeds or fails.
Therefore, let's look at a scenario that does involve implicit conversion. Let's start with your String class:
class String {
public:
explicit String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
};
Then let's define a function that receives a parameter of type String (could also be String const &, but String will do for the moment):
int f(String);
Your constructors allow implicit conversion from char const *, but only explicit conversion from int. This means if I call:
f("this is a string");
...the compiler will generate the code to construct a String object from the string literal, and then call f with that String object.
If, however, you attempt to call:
f(2);
It will fail, because the String constructor that takes an int parameter has been marked explicit. That means if I want to convert an int to a String, I have to do it explicitly:
f(String(2));
If the String(char const *); constructor were also marked explicit, then you wouldn't be able to call f("this is a string") either--you'd have to use f(String("this is a string"));
Note, however, that explicit only controls implicit conversion from some type foo to the type you defined. It has no effect on implicit conversion from some other type to the type your explicit constructor takes. So, your explicit constructor that takes type int will still take a floating point parameter:
f(String(1.2))
...because that involves an implicit conversion from double to int followed by an explicit conversion from int to String. If you want to prohibit a conversion from double to String, you'd do it by (for example) providing an overloaded constructor that takes a double, but then throws:
String(double) { throw("Conversion from double not allowed"); }
Now the implicit conversion from double to int won't happen--the double will be passed directly to your ctor without conversion.
As to what using explicit accomplishes: the primary point of using explicit is to prevent code from compiling that would otherwise compile. When combined with overloading, implicit conversions can lead to some rather strange selections at times.
It's easier to demonstrate a problem with conversion operators rather than constructors (because you can do it with only one class). For example, let's consider a tiny string class fairly similar to a lot that were written before people understood as much about how problematic implicit conversions can be:
class Foo {
std::string data;
public:
Foo(char const *s) : data(s) { }
Foo operator+(Foo const &other) { return (data + other.data).c_str(); }
operator char const *() { return data.c_str(); }
};
(I've cheated by using std::string to store the data, but the same would be true if I did like they did and stored a char *, and used new to allocate the memory).
Now, this makes things like this work fine:
Foo a("a");
Foo b("b");
std::cout << a + b;
...and, (of course) the result is that it prints out ab. But what happens if the user makes a minor mistake and types - where they intended to type +?
That's where things get ugly--the code still compiles and "works" (for some definition of the word), but prints out nonsense. In a quick test on my machine, I got -24, but don't count on duplicating that particular result.
The problem here stems from allowing implicit conversion from String to char *. When we try to subtract the two String objects, the compiler tries to figure out what we meant. Since it can't subtract them directly, it looks at whether it can convert them to some type that supports subtraction--and sure enough, char const * supports subtraction, so it converts both our String objects to char const *, then subtracts the two pointers.
If we mark that conversion as explicit instead:
explicit operator char const *() { return data.c_str(); }
...the code that tries to subtract two String objects simply won't compile.
The same basic idea can/does apply to explicit constructors, but the code to demonstrate it gets longer because we generally need at least a couple of different classes involved.
Here is this simple code
#include <map>
class MyMap : public std::multimap<int*, int*>
{
public:
void foo(const int* bar) const
{
equal_range(bar);
}
};
int main()
{
MyMap myMap;
int number;
myMap.foo(&number);
return 0;
}
It doesn't compile, and give the following error
error C2663: 'std::_Tree<_Traits>::equal_range' : 2 overloads have no legal conversion for 'this' pointer
I have seen many topic about this error, and it seems that it is a const issue. It compiles fine if I turn foo(const int* bar) into foo(int* bar).
Problem is, I don't see how foo content is supposed to change anything to my MyMap object. std::multimap proposes a const version of equal_range:
http://www.cplusplus.com/reference/map/multimap/equal_range/
What is my problem?
Thank you
Check the definition of equal_range:
pair<const_iterator,const_iterator> equal_range (const key_type& k) const;
It expects a constant reference to key_type: const key_type& k.
What you were trying to supply was a pointer to a constant integer: const int* bar
Why doesn't this work even though both values are const?
A constant reference to an integer const int& foo means that you cannot let foo refer to another integer, but it is allowed to change the value of the referenced integer.
A pointer to a constant integer const int* foo means that you can let foo point to another integer, but you cannot change the value of the integer it points to.
What the map actually expects is a const int*& k, but the map will automatically convert this if you supply a int* only (without the const).
[Edit]
Also note that a MyMap object still cannot be changed by your foo function even if you change const int* to int* as there still is another const at the end of your foo function. This const at the very end declares the function as constant meaning that it cannot modify the current object in which it is executed. If it was trying to modify it or call anything that could potentially modify it, you would get a compiler error. (Disclaimer: There are ways to modify a class from within a const function anyway but that's another topic.)
If fact the right compiler message can give you the answer. For one of the two overloads (the const one):
/usr/local/include/c++/v1/map:1836:41: note: candidate function not viable: 1st argument ('const int *')
would lose const qualifier
const int* is a pointer to a const int. The method expects a const key_type&, i.e. const int*& argument, that is a const reference to a (non-const) int*.
When confused, prefer to write const int* as int const*, which is the same thing. Then you'll see the difference to const int*& more clearly.
For your code to work, use int* instead of const int*.
I believe that the problem has to do with the mismatch on the key_type.
On one hand you have a multimap where key_type=int* while on the other hand you are passing a key_type=const int* thus attempting to drop the const qualifier on the key_type. I was confused by this too because I was expanding the key_type in my mind to get const int*& which should be compatible. However, the mismatch happens earlier on the key_type itself. At least that's the only logical explanation I could think of.
My suggestion would be to make the key_type const int* and keep your function parameter as is. After all, why would you need a pointer to a mutable value as a key to a map ?
I have a wrapper class and I want to modify the data and convert it back to its original type.
class A
{
public:
A ( unsigned __int64 _a ) : a (_a)
{
}
operator unsigned __int64 () const
{
return a;
}
unsigned __int64 a;
};
I want the object of this class to implicitly convert back to unsigned __int64, but it failed.
Say,
A a( 0x100ull );
unsigned __int64 b = (a >> 16); // Error
Compiler gives C2678 error, no operator found or there is no acceptable conversion.
It seems this function operator unsigned __int64 () const doesn't work.
To be more specific, compiler says there is no acceptable conversion. I cannot accept the complain, because I have already given a good one. Can someone legitimize it?
It doesn't work because you haven't created an operator>> overload for your class that takes an integer and does something with it.
I'm guessing you're trying to do a right shift on your int, but I'm not sure that overloading your operator>> is a good idea for that, as these operators in a context like that, are normally used for streaming. It might confuse a reader or maintainer of your code afterwards.
See here for more info on operator overloading
Perhaps rethink your implementation strategy?