I need to declare a good number of simple POD structures that will behave the same but that are really different types, i.e not typedefs.
Anyway I just want to keep them as simple as possible. But while testing I saw that the compiler performs some implicit conversions and I'd like to avoid this.
Given this code :
template<typename T>
struct Struct {
T data;
operator T() const { return data; }
};
void fun(Struct<float> value)
{
cout << "Call with Struct :: " << value << endl;
}
void fun(int value)
{
cout << "Call with INT :: " << value << endl;
}
int main(int, char**)
{
fun(3);
fun(4.1f);
fun(Struct<float>{5.2});
fun(Struct<double>{6.3});
return 0;
}
Compiled with GCC.
The execution gives me :
Call with INT :: 3 // Ok
Call with INT :: 4 // [1]
Call with Struct :: 5.2 // Ok
Call with INT :: 6 // [2]
How can I avoid implicit conversions [1] and [2] ?
Thanks
As requested in the comment :
The use of the explicit keyword for operator T() will actually prevent implicit type conversion.
Thus, declaring the struct this way:
template<typename T>
struct Struct {
T data;
explicit operator T() const { return data; }
};
will make the compiler prevent implicit conversion, and require that the client code specifically ask for a conversion (i.e. use T(Struct<T>) anywhere a T is required )
Related
I'm debugging an issue in a large C++ codebase where an attribute of a struct is occasionally being changed to a bad value. Unfortunately, the attribute is public and is accessed or changed in hundreds of places, so simply adding a breakpoint on a mutator is not possible. Also, I don't know the instance of the struct, so adding an address watchpoint wouldn't help.
Instrumenting the code would be a major job. However, a colleague helpfully suggested creating a proxy class which could wrap the existing type in the struct declaration. For example, instead of using MyType _type I would replace this with ChangeProxy<MyType> _type in the struct and the application should compile and work with the proxy taking the place of the direct type in the same manner as, for example, a smart pointer.
However, when I build an example, the implicit conversion operation in the template class doesn't appear to get invoked in type deduction. Here's the code:
#include <iostream>
class MyType {
long _n = 0;
public:
MyType() {}
MyType(const long n) : _n{n} {}
MyType& operator=(const long n) { _n = n; return *this; }
bool isZero() const { return _n != 0; }
};
template <class T>
class ChangeProxy {
public:
ChangeProxy() {}
ChangeProxy(const T& t) : _t{t} {}
ChangeProxy(const T&& t) : _t{std::move(t)} {}
ChangeProxy& operator=(const T& t) {onChange(t); _t = t; return *this;}
ChangeProxy& operator=(const T&& t) {onChange(t); _t = std::move(t); return *this;}
operator T() {return _t;}
private:
T _t;
void onChange(const T& newVal) { /* something here to notify me of changes */ };
};
struct MyStruct {
// MyType _type; // this works ...
ChangeProxy<MyType> _type; // .. but this doesn't
};
int main() {
MyStruct i;
std::cout << "i._type.isZero() : " << std::boolalpha << i._type.isZero() << std::endl;
i._type = 1;
std::cout << "i._type.isZero() : " << std::boolalpha << i._type.isZero() << std::endl;
return 0;
}
Unfortunately, when I build this I get the following errors:
proxy-variable~test.cpp:35:73: error: ‘class ChangeProxy<MyType>’ has no member named ‘isZero’
35 | std::cout << "i._type.isZero() : " << std::boolalpha << i._type.isZero() << std::endl;
| ^~~~~~
proxy-variable~test.cpp:37:73: error: ‘class ChangeProxy<MyType>’ has no member named ‘isZero’
37 | std::cout << "i._type.isZero() : " << std::boolalpha << i._type.isZero() << std::endl;
| ^~~~~~
So it seems that the compiler isn't deducing that it can cast a ChangeProxy<MyType> to a MyType. What have I done wrong here?
The context here doesn't let the compiler try out implicit conversions. Calling a member function on some object never does. You can force this by e.g.
std::cout << "i._type.isZero() : " << std::boolalpha <<
static_cast<MyType>(i._type).isZero() << '\n';
// ^^^^^^^^^^^^^^^^^^^ Here, enforce conversion
Another option would be:
MyStruct i;
const MyType& underlying = i._type; // Again, request conversion manually
std::cout << underlying.isZero() << '\n';
What you are doing is invoking a method on the class ChangeProxy<MyType> which indeed doesn't have any method isZero() defined on it, hence the compilation error. You could probably add something like
T const& operator()() const {return _t;}
And then call it using
i._type().isZero()
The reason that the wrapped i._type.isZero() can never work is that implicit conversions of the i._type object aren't considered for direct method calls, and you can't overload operator. like you can operator->.
It's nothing to do with type deduction, there's simply no mechanism in the language to do what you want.
Luckily, you're solving the wrong problem anyway.
... a colleague helpfully suggested creating a proxy class which could wrap the existing type in the struct declaration
Hmm, you didn't mention that here - or am I a colleague now?
an attribute of a struct is occasionally being changed to a bad value
Which attribute? Be specific!
In your code, you're treating the MyType instance as the problematic attribute. However, the only state in MyType is its long _n member.
Writing
class MyType {
ChangeProxy<long> _n = 0;
which is what I actually suggested when I referred to wrapping built-in types, avoids this problem entirely. You may of course need operator!= to make isZero work, but that's a normally overloadable operator.
Oddly the code in your question doesn't permit any mutation of _n anyway, so it's unclear how it can be getting a bad value. However, I assume this is just an artefact of a simplified example.
I'm learning some new concepts about c++ and I'm playing with them.
I wrote some piece of code that really confuses me in terms of how it works.
#include <iostream>
class aid {
public:
using aid_t = std::string;
void setaid(const std::string& s) {
aid_ = s;
}
const aid_t& getaid() const {
return aid_;
}
private:
aid_t aid_;
};
class c {
public:
using c_t = std::string;
void setc(const aid::aid_t& aid_val) {
if (aid_val.size() < 4)
c_ = "yeah";
else
c_ = aid_val + aid_val;
}
const c_t& getc() {
return c_;
}
private:
c_t c_;
};
template<typename ...Columns>
class table : public Columns... {
};
template <typename... Columns>
void f(table<Columns...>& t) {
t.setaid("second");
std::cout << t.getaid() << "\n";
}
void f2(table<aid>& t) {
t.setaid("third");
std::cout << t.getaid() << "\n";
}
int main() {
table<aid, c> tb;
tb.setaid("first");
std::cout << tb.getaid() << " " << "\n";
// f<c>(tb); // (1) doesnt compile, that seem obvious
f<aid>(tb); // (2) works?
f(tb); // (3) works too -- template parameter deduction
// f2(tb); // (4) doesnt work? worked with (2)...
}
The idea here is simple, I have some table with columns. And then I would like to create some functions that require only some set of columns and doesn't care if passed argument has some extra columns.
My confusion is mostly about points (2) and (4) in code... My intuition says it should be the same, why it isn't and (2) compiles and (4) doesn't? Is there any major topic I'm missing and should read up?
Is there a way to achieve this particular functionality?
In the second case, the compiler still deduces the rest of the template parameter pack, so that you get table<aid, c> & as the function parameter. This is different from (4) (table<aid> &).
[temp.arg.explicit]/9:
Template argument deduction can extend the sequence of template arguments corresponding to a template parameter pack, even when the sequence contains explicitly specified template arguments.
Line 1 causes error: conversion from ‘C<void()>’ to non-scalar type ‘C<void (*)()>’ requested. I know I can write it as line 2, but how can I use the make_class() and assign it to a variable?
#include <iostream>
using namespace std;
template<class T> class C {
T f;
public:
C(T ff) : f(ff) {}
};
template<class Ft> C<Ft> make_class(const Ft& f)
{
return C<Ft>(f);
}
void f()
{
cout << "f()" << endl;
}
int main()
{
// C<void(*)()> v = make_class(f); // line 1
C<void(*)()> v(f); // line 2
return 0;
}
Another question is from this link. The code is shown as follows. How to understand line 3?
template <typename F>
struct foo {
F f;
void call() {
f();
}
};
void function() {
std::cout << "function called" << std::endl;
}
int main() {
foo<void(*)()> a = { function }; // line 3: { } is an array?
a.call();
}
Thanks.
A function type and a pointer to function types are different types in the language. While in most contexts the former will decay to the latter, when used as template arguments they generate two unrelated types (different instantiations of a template yield unrelated types).
The deduced type is const reference to function, not pointer to function. A simple workaround is dropping the const & from the function signature which will force the decay to a pointer to function (you cannot pass functions by value).
Regarding the second question, that is called aggregate-initialization, which is in fact the same initialization performed for arrays (arrays being a subset of aggregates).
What's wrong with the code below? Latest version of g++ and clang both give error. I am sure I am missing something basic here.
#include <iostream>
struct Z
{
static const int mysize = 10;
};
Z f2();
int main()
{
std::cout << f2()::mysize << std::endl;
}
The motivation here is to be able to find out the size of an array using templates using code such as below. I know there are many ways, but just stumbled upon this idea.
template<int N> struct S
{
enum { mysize = N };
};
template<class T, int N> S<N> f(T (&)[N]);
int main()
{
char buf[10];
std::cout << f(buf)::mysize << std::endl;
}
f2() returns a value, not a type. You'd need to use the . operator on the return value instead of ::
The :: operator requires a type to be named on the lefthand side, while . allows for a value to be named. Your expression f2() does not name a type so it cannot be used in conjunction with ::.
As a side note, with a little more detail in the question we might be able to solve your real problem.
Your program contains two mistakes:
You are using the :: operator to access the member of an object. Use operator . ("dot") instead;
You declare function f2() and invoke it without defining it (this will give you a linker error).
Also, since static member variables are shared among all instances of a class (Z in this case), you do not need an object to access it;
Here is how you could fix your program:
#include <iostream>
struct Z
{
static const int mysize = 10;
};
Z f2() { return Z(); }
int main()
{
// Don't need an object to access a static variable...
std::cout << Z::mysize << std::endl;
// ...but if you really want to, do it this way...
std::cout << f2().mysize << std::endl;
}
Why don't you use this way to find out the size of array by templates:
#include <iostream>
template<int N> struct S
{
enum { mysize = N };
};
template<class T, int N> int f1(T (&)[N])
{
return N;
}
int main()
{
char buf[10];
std::cout << f1(buf) << std::endl;
}
And this one is closer to your variant:
template<class T, int N> S<N> f(T (&)[N])
{
S<N> foo;
return foo;
}
int main()
{
char buf[10];
std::cout << f(buf).mysize << std::endl;
}
Anyway, you will need to return an object from f and access it's member by ., not by ::.
But it's more probable that second variant will be slower, because first variant is fully compile-time, but in the second variant compiler may miss the optimization and don't optimize out the run-time creation of foo.
I think you need to add const int Z::mysize; after class declaration.
I have created a type list. I then create a class using a template passing the type list. When I call the print function of the class with a some types not specified they are casted. How can I enforce the exact type at compile time? So if I use an unlisted type I get a compiler error.
Thanks.
template <class T, class U>
struct Typelist
{
typedef T Head;
typedef U Tail;
};
class NullType
{
};
typedef Typelist<int,Typelist<float,Typelist<char*,NullType> > > UsableTypes;
template<class T>
class MyClass
{
public:
void print(T::Head _Value) { std::cout << _Value; }
void print(T::Tail::Head _Value) { std::cout << _Value; }
void print(T::Tail::Tail::Head _Value) { std::cout << _Value; }
private:
};
MyClass<UsableTypes> testclass;
void TestMyClass()
{
int int_val = 100000;
float flt_val = 0.1f;
char* char_val = "Hi";
short short_val = 10;
std::string str_val = "Hello";
testclass.print( int_val ); // OK 8-)
std::cout << endl;
testclass.print( flt_val ); // OK 8-)
std::cout << endl;
testclass.print( char_val ); // OK 8-)
std::cout << endl;
testclass.print( short_val); // this compiles OK and works ??? 8-(
std::cout << endl;
testclass.print( str_val ); // compile error 8-)
std::cout << endl;
}
#Kerrek SB: Hi I thought it was going to help me with my next step, which was creating the print function depending on the t_list contents, Types and amounts of types. But I'm struggling to separate compile time processing and runtime processing. What I am trying to do is create a print function for each type in the list. So if the list has two types, two print functions will be created and if there are five types then five print functions will be created one for each type.
When I do this:
typedef Typelist<int,Typelist<float,Typelist<char*,NullType> > > UsableTypes;
MyClass<UsableTypes> newclass
Does this create three instance of MyClass one for each type in the list or does it create one instance and I have to create a print function for each type?
I feel I almost have all the blocks in my mind but just can’t fit them together. Any help you can offer would be gratefully received. Thanks.
Add a private function template
template<typename T> void print(T);
which doesn't need an implementation. This should catch all types for which no explicit print exists, and since it is private, it will give an error message.
You would have to make your print function into a template and then check whether the types match:
template <typename U>
void print(const U & u)
{
// use std::is_same<typename std::decay<T::Head>::type, typename std::decay<U>::type>::value
}
Here I'm stealing is_same and decay from <type_traits>, but if you don't have C++11, you can either take them from TR1 or from Boost, or just write them yourself, as they're very simple type modifier classes.
The conditional would best go into a static_assert, which is another C++11 feature, but there exist similar constructions for C++98/03 that produce a compile-time error under a certain condition.
You could take your arguments by non-const reference, forcing them to be the exact same type. However you can no longer use it with const variables or literals.