I am using a library that is templated and that I do not wish to modify. Namely CImg.
This library has been mostly designed to work with templates of simple types: float, double, int etc.
At some point, this library does:
CImg<T>& fill(const T val) {
if (is_empty()) return *this;
if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
else std::memset(_data,(int)val,size()*sizeof(T));
return *this;
}
Now I want to use this library with a more complex class as template parameter. My particular class is such that sizeof(T)!=1 and most of the time, the fill function will properly assign val to each element with the proper operator= of my class. However, when !val, I would like a conversion operator that allows my class to be cast to an int and to produce some values (for example, 0 would make the function above work).
Right now, my program does not compile as it says:
error C2440: 'type cast' : cannot convert from 'const MyClass' to 'int'
How can I create an operator that allows for (int)my_variable with my_variable of type MyClass to be legal, without modifying the function above ?
Something like this using user defined conversions
int type;
explicit operator int()
{
return type;
}
What you want in this case is probably int conversion operator overload:
class A{
public:
explicit operator int() const{
return 2;
}
};
EDIT:
|I added explicit conversion that should make your code compile (at least the method you showed us), and not mess-up other operators, but it's only allowed since C++11, so if you are using older compiler it might not be available yet.
Related
I have a class that has both implicit conversion operator() to intrinsic types and the ability to access by a string index operator[] that is used for a settings store. It compiles and works very well in unit tests on gcc 6.3 & MSVC however the class causes some ambiguity warnings on intellisense and clang which is not acceptable for use.
Super slimmed down version:
https://onlinegdb.com/rJ-q7svG8
#include <memory>
#include <unordered_map>
#include <string>
struct Setting
{
int data; // this in reality is a Variant of intrinsic types + std::string
std::unordered_map<std::string, std::shared_ptr<Setting>> children;
template<typename T>
operator T()
{
return data;
}
template<typename T>
Setting & operator=(T val)
{
data = val;
return *this;
}
Setting & operator[](const std::string key)
{
if(children.count(key))
return *(children[key]);
else
{
children[key] = std::shared_ptr<Setting>(new Setting());
return *(children[key]);
}
}
};
Usage:
Setting data;
data["TestNode"] = 4;
data["TestNode"]["SubValue"] = 55;
int x = data["TestNode"];
int y = data["TestNode"]["SubValue"];
std::cout << x <<std::endl;
std::cout << y;
output:
4
55
Error message is as follows:
more than one operator "[]" matches these operands:
built-in operator "integer[pointer-to-object]" function
"Setting::operator[](std::string key)"
operand types are: Setting [ const char [15] ]
I understand why the error/warning exists as it's from the ability to reverse the indexer on an array with the array itself (which by itself is extremely bizarre syntax but makes logical sense with pointer arithmetic).
char* a = "asdf";
char b = a[5];
char c = 5[a];
b == c
I am not sure how to avoid the error message it's presenting while keeping with what I want to accomplish. (implicit assignment & index by string)
Is that possible?
Note: I cannot use C++ features above 11.
The issue is the user-defined implicit conversion function template.
template<typename T>
operator T()
{
return data;
}
When the compiler considers the expression data["TestNode"], some implicit conversions need to take place. The compiler has two options:
Convert the const char [9] to a const std::string and call Setting &Setting::operator[](const std::string)
Convert the Setting to an int and call const char *operator[](int, const char *)
Both options involve an implicit conversion so the compiler can't decide which one is better. The compiler says that the call is ambiguous.
There a few ways to get around this.
Option 1
Eliminate the implicit conversion from const char [9] to std::string. You can do this by making Setting::operator[] a template that accepts a reference to an array of characters (a reference to a string literal).
template <size_t Size>
Setting &operator[](const char (&key)[Size]);
Option 2
Eliminate the implicit conversion from Setting to int. You can do this by marking the user-defined conversion as explicit.
template <typename T>
explicit operator T() const;
This will require you to update the calling code to use direct initialization instead of copy initialization.
int x{data["TestNode"]};
Option 3
Eliminate the implicit conversion from Setting to int. Another way to do this is by removing the user-defined conversion entirely and using a function.
template <typename T>
T get() const;
Obviously, this will also require you to update the calling code.
int x = data["TestNode"].get<int>();
Some other notes
Some things I noticed about the code is that you didn't mark the user-defined conversion as const. If a member function does not modify the object, you should mark it as const to be able to use that function on a constant object. So put const after the parameter list:
template<typename T>
operator T() const {
return data;
}
Another thing I noticed was this:
std::shared_ptr<Setting>(new Setting())
Here you're mentioning Setting twice and doing two memory allocations when you could be doing one. It is preferable for code cleanliness and performance to do this instead:
std::make_shared<Setting>()
One more thing, I don't know enough about your design to make this decision myself but do you really need to use std::shared_ptr? I don't remember the last time I used std::shared_ptr as std::unique_ptr is much more efficient and seems to be enough in most situations. And really, do you need a pointer at all? Is there any reason for using std::shared_ptr<Setting> or std::unique_ptr<Setting> over Setting? Just something to think about.
I want to overload the operator = and I have the following operator-function
int IntegerClass::operator=(IntegerClass integer) {
return integer.number;
}
This should be correct?
In another class I want to assign the objects private member (int) to another int i.e.
int x = integerClass;
but when I compile I get the following error
error: cannot convert 'std::IntegerClass' to 'int' in initialization
What is wrong with my implementation of operator-overloading and how should the function look like?
Your operator overloads assignment of one IntegerClass to another, but you're trying to assign (actually it's initialization) to a built in int. You need to define an implicit conversion operator.
The code should be something like this (sorry I don't remember the exact syntax)
IntegerClass::operator int() {
return number;
}
I am trying to write bool-conversion operator for std::bitset
I tried:
template<size_t size>
operator bool(std::bitset<size> & b)
{
return b.any();
}
but I got
error C2801: 'mynamespace::operator bool' must be a non-static member
from my visual-studio.
But when I look up C2801 explanation it says nothing about conversion operators (only about =, ->, [],())
So, is it possible to somehow write "Conversion std::bitset to bool operator?"
(I can not call b.any() in my if-statements, because the same code must run when std::bitset is replaced with unsigned or something
typedef std::bitset<x> Bitset;
//typedef unsigned Bitset;
so the ideal syntax will be like:
Bitset b = whatewer;
if(b)
doStuff();
)
If this overloading is not possible, what is the recommended workaround?
so far I use it like:
if(b == Bitset(0))
doStuff();
but I dont like it.
Thank you
As the error message says, the conversion operator must be a non-static member of a class. That is true.
I can not call b.any() in my if-statements, because the same code must run when std::bitset is replaced with unsigned or something.
If that is your problem, then you can use function overload, and call it passing the argument which will return a boolean value:
template<typename T>
bool to_bool(T const & b)
{
return b; //implicit conversion (if allowed) for all other types
}
template<size_t N>
bool to_bool(std::bitset<N> const & b)
{
return b.any();
}
then use it as:
if (to_bool(whatever))
{
}
It will call the correct overload. If the type of whatever is std::bitset<N> then the second overloaded function will be called, or else the first one will be called.
§12.3.2/1: "A member function of a class X with a name of the form [...] specifies a conversion from X to the type specified..." (C++11 uses the same section number and nearly the same wording, adding only that the function takes no parameters).
The other possible way to define a conversion is a constructor (§12.3.1), which is obviously a class member as well.
In short, yes, conversions must always be defined as member functions.
One way to do what you want would be to write a wrapper around std::bitset that provides the conversion you care about:
template <int size>
class mybitest {
std::bitset<size> bits;
public:
operator bool() { return bits.any(); }
}
But if you decide to do that, you'll need to write forwarding functions for essentially all the pieces of bitset you're using (ctors, assignment, etc.)
The standard is a bit unclear on this (12.3.2):
A member function of a class X having no parameters with a name of the form [...] specifies a conversion from X to the type specified by the conversion-type-id. Such functions are called conversion functions. No return type can be specified. If a conversion function is a member function, the type of the conversion function (8.3.5) is “function taking no parameter returning conversion-type-id”.
The first sentence seems to imply that only member functions can be conversion functions, but I'm not sure what the purpose of the conditional "if a conversion function is a member function" is.
I'd take the first sentence as binding and conclude that a conversion function must be a member function.
in case this helps somebody, you can actually provide a not operator instead
template<size_t size>
operator !(std::bitset<size> & b)
{
return !b.any();
}
and use it like so using the !! idiom:
if (!!whatever)
{
}
still not ideal, but a bit closer I think.
Given a template pass-by-reference conversion/type-cast operator (without const) is possible:
class TestA
{
public:
//Needs to be a const return
template<typename TemplateItem>
operator TemplateItem&() const {TemplateItem A; A = 10; return A;}
};
int main()
{
TestA A;
{
int N;
N = A;
printf("%d!\n",N);
}
{
float N;
N = A;
printf("%f!\n",N);
}
return 0;
}
And given the following code (with const):
class TestA
{
public:
//Produces error
template<typename TemplateItem>
operator const TemplateItem&() const {TemplateItem A; A = 10; return A;}
};
Produces these errors:
error: cannot convert 'TestA' to 'int' in assignment
error: cannot convert 'TestA' to 'float' in assignment
Question
How do I make it so the conversion/type-cast operator return a const pass-by-reference of the template type?
Context
Before most people come in and freak about how 'you can't convert it to just anything', you'll need context. The above code is pseudo code - I'm only interested on const reference returns being possible, not the pitfalls of a templated conversion function. But if you're wondering what it's for, it's relatively simple:
TemplateClass -> Conversion (turned into byte data) -> File
TemplateClass <- Conversion (changed back from byte data) <- File
The user is expected to know what they are getting out, or it's expected to be automated (I.E. saving/loading states). And yes, there is a universal method for templates using pointers to convert any type into byte data.
And don't give me claptrap about std doing this sort of thing already. The conversion process is part of a more complicated class library setup.
I'm a programmer. Trust me. C++ trusts me and lets me make mistakes. Only way I'll learn.
Firstly, your conversion operator is already undefined behavior because you return a reference (const or not) to a local variable that has gone out of scope. It should work fine if you change your conversion operator to return by value which won't induce UB.
EDIT: (removed incorrect information about conversion operators).
But are you really sure that you really want your class type to be convertible to anything? That seems like it's just going to cause many headaches in the future when you're maintaining the code and it converts to an unexpected type automatically.
Another possible implementation is to create an as template method that basically does what your conversion operator wants to do and call it like obj.as<int>().
I have a class which wraps an enum for easy printing, serialization, etc.
I want to be able to use it in a switch statement as the traditional enum, hence I was using a int() overloader till gcc-4.3. However my code breaks now with gcc-4.5.1.
enum E { consta, constb };
class Wrap {
private:
E e;
public:
operator E() { return e;}
operator E() const { return e;}
operator int() const { return e;}
Wrap(E a) : e(a) { }
};
int main() {
Wrap x(constb);
x = consta;
switch (x) { /* Error here */
case consta: // ..
case constb: // ..
}
return 0;
}
Compiler errors are:
error: ambiguous default type conversion from 'Wrap'
error: candidate conversions include 'Wrap::operator E() const' and 'Wrap::operator int() const'
This is part of a library and I want the code to work over all versions, hence removing the int overloader was not an option.
If you're specifically working around compiler bugs, just use conditional compilation to create a workaround and keep it as self-contained as possible.
#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40300
typedef int E; // workaround
# define ENUM_TYPE // do not define an enum type as it's not fully supported
#else
# define ENUM_TYPE E // newer GCC supports enum better
#endif
enum ENUM_TYPE { consta, constb };
#undef ENUM_TYPE
… after this point, you can forget about the workaround, and only one operator is necessary …
operator E() const { return e;}
Change the line to
switch ((E)x) {
Enums and ints are very similar in the compiler (and probably identical in memory), so having both of those is confusing things. If you specifically cast it, here won't be any ambiguity in which you want to use.
You can resolve the ambiguity by selecting the conversion yourself with a static_cast:
switch(static_cast<E>(x))
Another option would be to make one or more of the conversion operators explicit -- that would resolve the ambiguity by limiting the compiler's options.
However, explicit conversion operators are available only for C++0x, which your compiler does not support.
Doesn't C++ allow you to automatically convert from an enum to int, rendering the operator int unneeded (for example, it would use the E conversion and then convert to int)? I know you said you don't want to remove it but did you actually confirm it breaks API compatibility?
If that's not an option probably the best bet is to add as_int and as_E methods to use in contexts where the desired conversion type is ambiguous.
The spec says for the condition (switched-over value):
The condition shall be of integral type, enumeration type, or of a class type for which a single conversion function to integral or enumeration type exists.
Your class type has 3 conversion functions to integral or enumeration types, so it easily fails this constraint. It won't even work if you remove the operator int overload, because then there are still 2 conversion functions. No overload resolution happens. There is also no need for a non-const operator E overload if it doesn't write anything anyway
An explicit cast will be a safe bet. Same rather weird way is to use op+, but which I wouldn't take for clarity reasons
switch (+x) { /* Don't do this in the wild */
case consta: // ..
case constb: // ..
}