53

There are known ways to manipulate the type of an integer literal

0L;  // long
3U;  // unsigned integer
1LL; // long long

What I need is a way to initialize an integer literal to std::size_t. I supposed that doing

2U; // unsigned int

would be enough, but I still get a compiler error when calling a function template that expects two arguments of the same integral type (no matching function to call for func(unsigned int, size_t)

I know/verified that explicitly casting ( static_cast<std::size_t>(1) ) the first argument solves the problem but I'm asking if there's a prettier solution

EDIT

the function has a signature

template <class T> const T& func(const T& a, const T& b);

EDIT2

I don't know if this question is to "blame" but I'm happy to announce that this is upcoming (cudos @malat for mentioning this in the comments)

Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
  • 2
    How do you call `func`? Can you show your code? – songyuanyao Mar 12 '14 at 08:58
  • 2
    Please show both the call and the declaration of the function you want to call. Also please show the *complete* and *unedited* error log. And *edit your question* to include that information. – Some programmer dude Mar 12 '14 at 08:59
  • @dalle That would be unportable to C, where `'0'` has type `int`. – Potatoswatter Mar 12 '14 at 09:04
  • 2
    This is [upcoming](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4254.html) – malat Oct 15 '15 at 10:29
  • @malat: What is the state of the proposal? Which language version will it appear? – Silicomancer May 28 '19 at 20:44
  • 2
    @Silicomancer: According to the Revision History [here](http://open-std.org/JTC1/SC22/WG21/docs/papers/2019/p0330r5.html), the last update was that it's being targeted for C++23 (as of June 17th, 2019). – TManhente Sep 11 '19 at 20:31
  • Sad truth is, that `size_t` was somehow forgotten when someone defined the header ``. Had it not been forgotten, I muse, this would be the place for some `SIZE_C()` macro in the spirit of the others defined there, such as `UINT8_C()`. `UINTMAX_C()` comes closest to it as `size_t` is defined to be able to hold the biggest available integer type. – BitTickler May 05 '21 at 10:01

4 Answers4

74

There is no such standard facility. C99 and C++11 implementations do have such macros in <stdint.h>/<cstdint>. But even there, the macros are only defined for the stdint.h types, which do not include size_t.

You could define a user-defined literal operator:

constexpr std::size_t operator "" _z ( unsigned long long n )
    { return n; }

auto sz = 5_z;
static_assert( std::is_same< decltype( sz ), std::size_t >::value, "" );

The constexpr is necessary to use it in array bounds int arr[ 23_z ] or case 9_z: labels.

Most would probably consider the lack of macros to be an advantage :) .


Cuteness aside, the best way is to use brace initialization: std::size_t{ 42 }. This is not equivalent to std::size_t( 42 ) which is like a nasty C cast — presumably what you were avoiding with static_cast. Quite the opposite: the braces require that the value inside is exactly representable in the targeted type. So, char{ 300 } and std::size_t{ -1 } are both ill-formed.

Braces and parens look similar, but they're polar opposites in safety when initializing temporaries. Braces are safer than the literal operator could ever be, since unlike a function they can discriminate compile-time values.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • This solution would be unportable to C... ;) – dalle Mar 12 '14 at 10:19
  • 1
    @dalle Less trickily so, though. Of course, your method could be made portable simply by having a global `char c;` and doing `5 * sizeof(c)`; amenable to macros for those so inclined. – Potatoswatter Mar 12 '14 at 12:10
  • @Potatoswatter Both answers got +1 but this is chosen for additionally using `constexpr` – Nikos Athanasiou Mar 12 '14 at 17:05
  • 4
    +1 you should propose this for library inclusion using `z` instead of `_z`. It would simplify things like `for (auto i = 0z; i < v.size(); ++i)` – TemplateRex Sep 30 '14 at 18:30
  • @TemplateRex It's been informally proposed on the std-discussion list/Google group. There was not much support for the idea. Personally I think this is part of the "almost" in "almost always auto." – Potatoswatter Oct 01 '14 at 00:07
  • [The aforementioned std-proposals discussion](https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/tGoPjUeHlKo) if anybody wants to read it. – Morwenn Mar 12 '15 at 15:16
  • Is there any way to use this with with a `#define` statement. E.g., I have `#define BLOCKSIZE 262144`. `BLOCKSIZE_z` doesn't work. – thc Nov 04 '18 at 22:27
  • @thc You can use the `##` operator of the preprocessor to append `_z` after substituting `BLOCKSIZE`, but that can only be done inside a macro. At that point you might as well just cast. – Potatoswatter Nov 04 '18 at 23:20
  • I would disagree on std::size_t( 42 ) being a C-cast. (std::size_t)42 is the cast syntax. std::size_t( 42 ) is just a ctor call. the same as being used for members in ctor initializer lists for example ( before uniform initialization came along this was the only option ) – QBziZ Nov 05 '18 at 08:49
  • @QBziZ An initializer list uses {} and a ctor call uses (). These are two different things. The values in either of these two things undergo only implicit conversions. The constructor-like syntax `size_t(value)` denotes explicit conversions so it’s a third different thing. – Potatoswatter Nov 05 '18 at 09:17
  • @Potatoswatter I do not disagree with what you say. My point is that A::A() : somemember( 5 ) {...} implies a ctor call for somemember. somemember could be size_t. this indeed converts, and thus can be seen as a conversion ctor, not a cast. – QBziZ Nov 05 '18 at 10:10
  • @QBziZ That is not how the standard uses the term “constructor” and especially not “conversion constructor.” – Potatoswatter Nov 05 '18 at 10:37
  • @Potatoswatter that is true as well, ctors are not defined for built-in types. but the ctor syntax does apply to them and the results are the same. there is even a default ctor for them. all of this is done explicitly to leverage generic programming for built-in types. so formally size_t(5) is not a ctor, but it would do the community well to not consider them as ugly C-casts. – QBziZ Nov 05 '18 at 10:46
  • @QBziZ [citation needed] – Potatoswatter Nov 05 '18 at 10:47
  • https://stackoverflow.com/questions/5113365/do-built-in-types-have-default-constructors. The comments are where a lot of info resides as well. And indeed, not according the C++ Standard but I do interpret this grey zone in a pragmatic manner. – QBziZ Nov 05 '18 at 13:01
  • 1
    @QBziZ Okay, that page supports your argument if you ignore the answers and read the comments instead. Except I just read the comments and they still don't support or mention the idea that default-initialization of a scalar type involves a constructor, which is what you've asserted here. – Potatoswatter Nov 05 '18 at 13:54
  • "Braces are safer than the literal operator could ever be, since unlike a function they can discriminate compile-time values." We can use a `` literal operator. That would be a lot of work, but it is technically possible (except for the most negative value, in which case the `-` cannot be parsed by the literal operator). – L. F. Mar 07 '20 at 02:16
44

Starting from C++23 you can use the uz or UZ literal for size_t.

auto size = 42uz;
static_assert(std::is_same_v<decltype(size), std::size_t>);

Paper: P0330R8
cppreference: Integer literals

Synck
  • 2,727
  • 22
  • 20
35

There is no dedicated suffix for std::size_t. In C++11, you could create a user-defined literal for it, though:

std::size_t operator "" _sz (unsigned long long int x)
{
  return x;
}

// Usage:

auto s = 1024_sz;

static_assert(std::is_same<decltype(s), std::size_t>::value, "He's wrong");

Live example

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
9

Depending on the function, you may also be able to do this and may find it cleaner:

auto result = func<size_t>(1, some_var);

For example, I've done this with std::max:

auto result = std::max<size_t>(0, std::min<size_t>(index, vec.size()-1));

By explicitly specifying the template instantiation, the casts can be implicit. However, do note that this is a cast, so is susceptible to errors which Potatoswatter's brace initialization isn't.

Community
  • 1
  • 1
Neil Traft
  • 18,367
  • 15
  • 63
  • 70