I'm using the following template to encode unsigned char values:
template <unsigned char val>
struct Cell {
enum { value = val };
using add = Cell<val + 1>;
using sub = Cell<val - 1>;
};
I expected sub to behave like a standard unsigned char variable regarding overflow:
unsigned char x = 0;
x - 1; // 255
But instead I get a compiler error in Clang:
using cell = Cell<0>;
cell::sub::value; // Error here.
Non-type template argument evaluates to -1, which cannot be narrowed to type 'unsigned char'
Is overflow handled differently in template contexts?
val - 1 is an int on your platform, on account of the usual arithmetic conversions. There is no sensible meaning for a template parameter of type unsigned char to be given an int argument.
Simply make sure that your template argument has the desired type:
using sub = Cell<static_cast<unsigned char>(val - 1U)>;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^
(Using an unsigned int literal means that the usual conversions produce an unsigned int, which has well-defined narrowing semantics.)
Related
My code includes the following, and I get the error message above based on the last line below.
struct List {
int word_i;
int mod_i;
char mod_type;
char mod_char;
};
struct Morph {
Options mode;
deque<List> search_list;
vector<string> dictionary;
vector<bool> discovered;
string output;
int sel_word_i = 0;
bool end_found = 0;
};
// later on in a function:
morph->search_list.push_back({ morph->dictionary.size() - 1, 0, 0, 0 });
You can replace the last line with:
morph->search_list.emplace_back( morph->dictionary.size() - 1, 0, 0, 0 );
Thus the object is created not through brace initialization which does not allow narrowing conversion.
The narrowing conversion is from the return value of the call to size which returns std::size_t which is unsigned.
For why size() - 1 is not converted to a signed value see: C++ Implicit Conversion (Signed + Unsigned)
When and after you applied what Amir suggested, you may get an error saying something like, "this function does not take (3) arguments." To fix that you'll have to declare a constructor in the class, which you used for your vector, that takes that particular number of arguments. From what I understood when you replace push_back(); with emplace_back();the compiler thinks that you're trying to pass some variables to the constructor, which are the supposed arguments.
In terms of modern C++, is it possible to specify storage of a integer numeral to a char?
I am looking to write auto var = 255; to initialize var as unsigned char type
It is possible to specify other types of storage:
auto var_ulong = 255ul;
auto var_float = 255.f;
However, not short or char? Surely I am missing something...
There is no standard integer literal to do what you want, but you can add your own user- defined literal if you plan to do this a lot:
namespace foo {
// constexpr pre C++20
// |
// V
consteval unsigned char operator"" _uchar(unsigned long long x) {
return static_cast<unsigned char>(x);
}
}
using namespace foo;
auto var_uchar = 255_uchar;
Just be aware that normal integer promotion will still be in effect, so below, you'll get an int:
auto var_int = 55_uchar + 10_uchar;
Case 1:
using unumber = unsigned long;
unumber uvalue {};
Case 2:
using udigit_t = unsigned char;
using ubigvalue_t = vector<udigit_t>;
ubigvalue_t ubig_value;
Does using work in this case like an inferred type?
Basically, using is another keyword for typedef (among other things) since C++11. Both of these are used to create a type aliases. Therefore,
using udigit_t = unsigned char;
and
typedef unsigned char udigit_t;
are equal expressions, which will allow you to use udigit_t in the same sense as if you wrote unsigned char instead (with some pitfalls). For more information, take a look here:
Type alias, alias template.
using can be used as an alternative to typedef for defining type aliases in C++11 (no C++03/98). In some cases it makes your code easier to look. Compare these examples.
typedef unsigned char byte, *pbyte, &rbyte;
using byte = unsigned char;
using pbyte = unsigned char*;
using rbyte = unsigned char &;
typedef int (*fun)(int, int);
using fun = int (*)(int, int);
// And when later...
fun f = [&](int a, int b){return a + b;};
In this case, using does not state an inferred type, but a clearly defined type. To let the compiler infer and decide the type of a variable, use auto (C++11 only). So you can write
auto f = [](){return 1;};
// No more need to write this below
int (*f)(void) = [](){return 1;};
I'm trying to define my own datatype (called sfloat) that's similar to a float, but uses a different number of mantissa bits and exponential bits to better suit my data range and precision. The goal is to define a new datatype that can replace the float in already existing applications. Everything's working out so far, except that I have been unable to override or define the unsigned operator such that
unsigned sfloat(3.141527)
would return the unsigned version of this class, usfloat(3.141527).
It seems like the unsigned specifier might be able to be overloaded since VS intellisense is not complaining in the header file:
sfloat::sfloat(float f) { m_data = get16bit(f); }
operator unsigned() { /*Do stuff here */ };
But it's not working in declaration and initialization:
unsigned sfloat myPi= 3.141527; // Error: expected a ';'
I don't even know if this is possible to do in C++, and I'm curious if anybody has done this before?
Due to C++ default-int for signedness, operator unsigned () is just a syntactic shorthand for operator unsigned int (). User-defined types cannot be declared signed or unsigned.
There is no direct way to accomplish what you're trying to do. As #Angew mentioned in his answer, unsigned cannot be applied to user-defined types.
On the other hand, you could fake this up by defining types named sfloat and unsigned_sfloat which had conversions defined between them. You could then write
unsigned_sfloat x(137.0f); // Close enough. ^_^
And then define a conversion operator as
operator unsigned_sfloat() {
... implementation here ...
}
This gives you something syntactically close to what you want and works around the fact that the language does not let you use the unsigned keyword to modify a custom type.
Hope this helps!
You could mock something like this up with templates:
#include <type_traits>
template <typename T = int>
class myfloat
{
static_assert(std::is_same<T, int>::value, "myfloat should only be instantiated on \"signed\" and \"unsigned\" ints");
const bool isSigned = true;
// the rest of the signed implementation
};
template <>
class myfloat<unsigned>
{
const bool isSigned = false;
// the rest of the unsigned implementation
};
int main()
{
myfloat<> a; // signed
myfloat<signed> b; // signed
myfloat<unsigned> c; // unsigned
// myfloat<float> d; // <-- compile error
return 0;
}
Try the following:
template<typename T>
struct Unsigned;
and use it like:
Unsigned<sfloat> usfloat
Now, you have to specialize Unsigned for your type Float, but this should communicate "is an unsigned version of Float" slightly better than an unsigned_sfloat type. I'd only bother with this if you where building an entire library of such types you might want to attach Unsigned<> to, however.
I'm trying to create a compile-time bit mask using metaprograming techniques, my idea is to create something like this:
unsigned int Mask3 = Mask<2>(); // value = 0x03 = b00000000000000000000000000000011
unsigned int Mask3 = Mask<3>(); // value = 0x07 = b00000000000000000000000000000111
unsigned int Mask3 = Mask<7>(); // value = 0x7F = b00000000000000000000000001111111
The code that I'm trying is this:
template <const unsigned int N> const unsigned int Mask()
{
if (N <= 1)
{
return 1;
}
else
{
return ((1 << N) | Mask<N - 1>());
}
}
return 1;
But it result in tons pairs of warnings:
warning C4554: '<<' : check operator precedence for possible error
warning C4293: '<<' : shift count negative or too big
And in the end, the compile error:
error C1202: recursive type or function dependency context too complex.
So, I deduce that the recursivity never ends and falls into a compiler infinite loop but I'm don't understanding WHY.
As has already been pointed out, you're depending on a runtime check to
stop a compile time recursion, which can't work. More importantly,
perhaps, for what you want to do, is that you're defining a function,
which has no value until you call it. So even after you stop the
recursion with a specialization, you still have a nested sequence of
functions, which will be called at runtime.
If you want full compile time evaluation, you must define a static data
member of a class template, since that's the only way a compile time
constant can appear in a template. Something like:
template <unsigned int N>
struct Mask
{
static unsigned int const value = (1 << (N - 1)) | Mask<N - 1>::value;
};
template <>
struct Mask<0>
{
static unsigned int const value = 0;
};
(I've also corrected the numerical values you got wrong.)
Of course, you don't need anything this complicated. The following
should do the trick:
template <unsigned int N>
struct Mask
{
static unsigned int const value = (1 << (N + 1)) - 1;
};
template <>
struct Mask<0>
{
static unsigned int const value = 0;
};
(You still need the specialization for 0. Otherwise, 0 means all bits
set.)
Finally, of course: to access the value, you need to write something
like Mask<3>::value. (You might want to wrap this in a macro.)
It doesn't need to be recursive. This should work just fine :
template <const unsigned int N> const unsigned int Mask()
{
return ((1 << N) - 1);
}
It doesn't even need to be a template really. An (inlined) function is ok.
Note that if you want to support any value of N, specifically N >= sizeof(unsigned int) * CHAR_BIT, you probably want to treat those as a special case.
A template is created at compile time, but you are relying on run time behavior to stop the recursion.
For example, if you instantiate Mask<2>, it is going to use Mask<1>, which is going to use Mask<0>, which is going to use Mask<-1>, etc.
You have a runtime check for N being <= 1, but this doesn't help when it's compiling. It still creates an infinite sequence of functions.
To blunt template instantiation recursion you need to introduce one explicit specialization:
template <0> const unsigned int Mask()
{
return 1;
}
Your recursion never ends, because compiler tries to generate template implementation for both if-branches. So, when it generates Mask<0> it also generates Mask<0xffffffff> and so on
C++11 -- no recursion or templates:
constexpr unsigned mask(unsigned N) { return unsigned(~(-1<<N)); }
So far the answers only addressed the second error (C1202), but you asked more than that.
Warning C4554 is caused by a Microsoft compiler bug involving template parameters and the << operator. So, (1 << N) generates a warning. If N were an ordinary parameter, there would be no warning of course.
The very simple workaround is to use (1 << (N)) instead of (1 << N), and C4554 goes away!