Why template chooses T equals int when implicity passed uint16_t? - c++

I have next snippet
class Mapper { //not templated!
...
template<class T>
static QList<quint16> toPduValue(T value)
{
constexpr quint8 registersPerT = sizeof(T) / sizeof(quint16);
return buildPduValue((registersPerT < 1) ? (quint16) value : value);
}
template<class T>
static QList<quint16> buildPduValue(T value)
{
...
}
...
}
But when to toPduValue passed bool, and then to buildPduValue passed (quint16) value buildPduValue specializes like <int>?
callstack
debugger shows next expression

The type of your ternary expression is int. You can verify this by trying to compile this code and looking carefully at the error message:
#include <stdint.h>
char * foo = (2 < 3) ? (uint16_t)2 : (bool)1;
The error message will be something like:
error: invalid conversion from 'int' to 'char*'
You could just apply a cast to your ternary expression to cast it to the specific type you want.
Or you could ensure that both possible values of the ternary expression have the same type, so that the overall expression will have the same type.
Or you could use a if statement instead of a ternary expression.
Note that your ternary expression can only have one type; it doesn't have a different type depending on which case was evaluated.

Related

C++ template specialization not working when comparing sizeof(type) == constant

#include <cstdint>
#include <iostream>
uint32_t ReadInt() { return 0; }
uint64_t ReadLong() { return 0; }
template<class SizeType>
class MyType
{
public:
SizeType Field;
MyType()
{
Field = sizeof(SizeType) == 8 ? ReadLong() : ReadInt();
}
};
int main()
{
MyType<uint32_t> m;
std::cout << m.Field;
}
I get a compiler warning because it looks like the the condition sizeof(MyType) == 4 is not being evaluated at compile time.
If it were we would have specialization and this wouldn't be a problem.
Anyway I could achieve this?
EDIT: What I really want to achieve is this:
Field = ReadLong();
OR
Field = ReadInt();
through template meta-programming. Can I do this without specializing the Read* functions? Given how powerful c++ templates I really feel like I'm missing some aha moment, because if I have to specialize Read* functions the rabbit hole continues to go deeper.
warning C4244: '=': conversion from 'int64_t' to 'uint32_t', possible loss of data for the int specialization
I get a compiler warning because it looks like the the condition sizeof(MyType) == 4 is not being evaluated at compile time.
No, it doesn't look like that at all. Whether the comparison is evaluated at compile time or not has no effect on the return type of the conditional expression.
If we simplify the example by removing the conditional and expand the template using the type that you used, we get this minimal reproduction:
uint32_t Field;
int64_t temp = ReadInt(); // temporary variable for exposition
Field = temp;
Clearly, the int64_t may contain a value that is much greater than can be represented by int32_t. If it does, then the correct value will be lost in conversion. The compiler warns about this possibility.
Why is the type of the conditional int64_t specifically you might ask. Well, the types of each subexpression is int64_t and uint32_t respectively, so the type must be one of those. The type of Field or the result of the comparison - even if evaluated at compile time - has no effect on which type is chosen. Instead, integral promotion rules are used in this case.
Anyway I could achieve this?
A template function should work:
template<class T>
T Read() {
static_assert(false, "not implemented");
}
template<>
uint64_t Read<uint64_t>() {
return ReadLong();
}
template<>
uint32_t Read<uint32_t>() {
return ReadInt();
}
// ...
Field = Read<SizeType>();
I get a compiler warning because it looks like the the condition sizeof(MyType) == 4 is not being evaluated at compile time
Wrong, it is evaluated at compile-time, but the conditional operator still has the bigger result-type of the arguments.
To fix it, just add a cast:
Field = sizeof(SizeType) == 8 ? (SizeType)ReadLong() : ReadInt();
Another approach using overload-resolution:
long ReadData(long) { return ReadLong(); }
int ReadData(int) { return ReadInt(); }
Field = ReadData(SizeType());
A conditional operator ?: expression evaluates to a single type, which accommodates both possible internal-to-the-construct result types.
This happens even when it's evaluated at compile time.
The common type in your case is the largest of the two possible integer types.

Expansion of types from comparison

I know the following code does not work, and I fully understand why. What I actually do not understand is why not:
int main(int argc, char *argv[]) {
std::cout << (atoi(argv[1]) ? "foo" : 'b') << std::end;
}
Why: Of course, this expression may generate either a string or an integer, and this is the error pointed by the compiler:
error: operands to ?: have different types ‘const char*’ and ‘char’
Why not: Since the operator<< have a bind with both of the types const char* and char, why is it the compiler don't perform a code expansion as in a template -- which, I guess, is what is performed.
For example, if I had:
template <class T>
void a(const T& value) {
std::cout << a << std::endl;
}
I could call either a("foo") and a('b'), and -- I guess -- the compiler would do one expansion of the function with the type name [T = const char*] and another one with [T = char].
This may be a simple matter of what C++ does -- and what it does not --, but I fail to see if there's any corner case that would come up as an error if the expansion was performed.
C++ is a compiled statically-typed language and the type of an expression must be known at compile-time. The expression atoi(argv[1]) ? "foo" : 'b' could be a const char* or char, depending on the value of argv[1], which can't be known at compile-time. It's only when the program is actually executed that this value is known. So when the compiler is attempting to turn this expression into machine code, it can't decide which type to treat the expression as.
To see that it really doesn't have anything to do with the operator<<, just have the expression by itself:
int main(int argc, const char* argv[])
{
atoi(argv[1]) ? "foo" : 'b';
}
Even this won't compile, giving the following error:
error: operands to ?: have different types ‘const char*’ and ‘char’
It has nothing to do with cout or operator <<. The expression
atoi(argv[1]) ? "foo" : 'b'
itself wouldn't compile. The 2nd and 3rd operators that you feed to ?: must be either the same type, or types that are implicitly convertible to one other.
This is what you think you should be asking for:
#include <iostream>
#include <utility>
#include <type_traits>
#include <functional>
template<typename Left, typename Right>
auto tri( bool b, Left&& left, Right&& right )
-> decltype( std::forward<Left>(left)() )
{
if (b)
return std::forward<Left>(left)();
else
return std::forward<Right>(right)();
}
int main(int /*argc*/, char *argv[]) {
tri(
atoi(argv[1]),
[]()->std::ostream&{ return std::cout<<"foo"; },
[]()->std::ostream&{ return std::cout<<'b'; }
) << std::endl;
}
but it isn't what ? does.
C++ could be modified to do what you are asking, but the type cascade would grow boundlessly. Each time you have an expression that could return type A or type B, the calling code would have to be forked, which could cause further forking.
Signatures of functions would have to be expanded to list all of the types it "could" return.
Now, while this may be a worthwhile feature for C++ in the future, it isn't what C++ does now. Each expression in C++ has a single, definite type -- in template code, this occurs when you have instantiated the template.
As an aside, the ability to have poly-type return values in C++ would give you capabilities similar to exception handling, where a function could return a value or an error flag. As the calling code would have to automatically fork whenever you call a poly-type return value function, it would have to handle that error flag (either by returning it as an alternative type, or by handling it locally).

is this a variable or function

I was just looking through implementation of non local means algorithm via google (thanks google for code search) and come across this function mirror.
template<typename T,typename U,bool N>
inline int
boundaryExpansion::helperBase<T,U,N>::mirror(const int src,
const int size,
const int last) const {
const int32 alpha(src%size);
if (alpha>=0) {
return (((src/size) & 0x00000001) != 0) ? last-alpha : alpha;
}
return (((src/size) & 0x00000001) == 0) ? -alpha-1 : size+alpha;
}
And the line I am interested in is this
const int32 alpha(src%size);
Now what is alpha here? A function or a variable? What this syntax means? Is this a variable declaration?
This is a variable declaration. A declaration of the form:
type variablename = value;
is essentially equivalent to:
type variablename(value);
This is the case regardless of what type is - whether it is a user-defined class or a built-in type. Note that the reverse is not always the case - the = syntax requires that there be an accessible copy constructor.
For similar reasons, you can cast arithmetic types using the constructor syntax, as in: x = int(42.0);
It is a variable declaration, and it is equivalent to this:
const int32 alpha = src%size;

c++ error; how should i interpret its meaning?

This may be a silly thing to ask but I am confused with compilation error while trying to use the safe bool idiom while reading this article. Below is my code and I have indicated the lines where i get errors in main() function.
// is OK case
class BoolVer_OK {
bool m_OK;
public:
BoolVer_OK(bool ok) : m_OK(ok){}
operator bool() { return m_OK; }
};
// Not OK Case
class BoolVer_NotOK {
bool m_notOK;
public:
BoolVer_NotOK(bool ok) : m_notOK(!ok){}
bool operator !() const{ reportexecution; return !m_notOK; }
};
main()
{
BoolVer_OK ok(true);
BoolVer_NotOK notOK(true);
ok<<1; // Line#1 is valid
notOK << 1; // Line#2: error: inavlid operand to binary expression ('BoolVer_notOK' and 'int')
return 0;
}
Why we didn't get error at #Line1 while we get at #Line2. Both results in a boolean value before << operator.
ok supports operator bool, and C++ has this nice functionality called implicit casting and also promotion, and in this case for the binary shift operator <<, the bool is promoted to an int, and this is then shifted by 1.
In the second case, you've not provided that operator, and hence there is nothing to implicitly convert (and promote) to int, and you get the error. Try calling !notOk before the shift, now there is a bool, which will be promoted.
I don't think the compiler would automatically insert a call to operator! and then negate that to get you the bool you want. From what I see in the link you provided, they do their tests with a double negation, !!.
ok<<1; // Line#1 is valid
notOK << 1; // Line#2: error: inavlid operand to binary expression ('BoolVer_notOK' and 'int')
This happens because ok is converted to bool implicitly (overloaded operator), whereas notOK doesn't have that operator.
Test out the following code:
BoolVer_OK ok(true);
BoolVer_NotOK notOK(true);
int z = ok<<1; // is valid
//notOK << 1; // error: inavlid operand to binary expression ('BoolVer_notOK' and 'int')
int x = false << 1;
return 0;
The booleans on the left-side of the shift operator are converted to ints and then shifted.

Why would I need conversion?

In this code:
template<class T>
struct Side
{
};
template<class T>
struct LeftSide : public Side<T>
{
};
template<class T>
struct RightSide : public Side<T>
{
};
Side<int>* f(int left, int right)
{
return left < right ? new LeftSide<int> : new RightSide<int>;//<---Here I'm returning either left or right side
}
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
I'm getting an error:
_Error 1 error C2446: ':' : no conversion from 'RightSide *' to 'LeftSide *'_
I've thought (wrongly as I see) that I can assign pointer from derived to base without any problems. So where is the problem?
The problem is not with the conversion from either LeftSide or RightSide to Side<T>. As you originally thought, this conversion would be fine.
Rather, the problem is with this expression:
left < right ? new LeftSide<int> : new RightSide<int>
Let's break this down a little. The ternary operator (properly referred to in the Standard as the 'comparison operator') looks like this:
bool_val ? lhs_expression : rhs_expression
Keep in mind that this whole construct is itself an expression. Meaning it returns a value, which has to have a type, obv. The type of the whole expression is deduced from the types of lhs_expression and rhs_expression. In this case, you have a LeftSide and a RightSide. So, here's your problem.
LeftSide and RightSide are not directly related to each other other than having a common base class, and there's no conversion available between them. (You'd have to write one.) So there's no single datatype that the bool_val ? lhs_expression : rhs_expression can have. You might think, "well, silly compiler, why not just figure out the common base class and use that?" This is, indeed, a bit of a pain. Leaving the argument of it being Right or Wrong aside, it just doesn't work that way.
You have two options.
One, use a simpler construct:
if( left < right )
return new LeftSide<int>;
else
return new RightSide<int>;
Two, if you really really want to use the ternary operator (which is the case sometimes), you need to spoon-feed the compiler it's datatypes:
Side<int>* f(int left, int right)
{
return left < right ? static_cast<Side<int>*>(new LeftSide<int>) : static_cast<Side<int>*>(new RightSide<int>);// now you're good
}
I think that the ? : operator requires that the 2 choices be the same type; not that they can be converted to the same type
FYI gcc fails the same
error: conditional expression between distinct pointer types ‘LeftSide<int>*’ and ‘RightSide<int>*’ lacks a cast
casting both to Side(int)* works (but you probably knew that already)
You want both branches to return a Side<int>*, but the compiler doesn't know that, the type Side<int> doesn't appear anywhere in that expression.
Since I don't like to use a cast when an implicit conversion exists, I'd write this as:
if (left < right) return new LeftSide<int>;
return new RightSide<int>;
But if you want to use a ternary operator,
Side<int>* i_want_this_type;
return (left < right) ? new LeftSide<int> : (i_want_this_type = new RightSide<int>);
Now the right hand branch is type Side<int>*, the left hand is convertible to that type, everything is ok (and the compiler optimizes out the extra variable).
The two should be the same type, or one should be convertible to the other.
return left < right ? (Side<int>*)new LeftSide<int> : (Side<int>*)new RightSide<int>;