I came across some code that has confused me. I have distilled the confusion down to a simple case
template<typename T>
struct S
{
T member;
static const size_t size;
};
struct U { int i; };
typedef S<U> US_t;
template<> const size_t US_t::size = sizeof(U);
Q1: Why do I need the "template<>" on that last line, since US_t is fully described?
The code I actually came across - which compiles - has the equivalent of this:
template<>template<> const size_t US_t::size = sizeof(U);
I'm pretty sure it was a cut-and-paste error that wasn't caught because it compiled.
Q2: Is this valid? If so, why?
[I notice that codepad.org will compile the code with "template<>template<>".]
Imagine your typedef as a macro that's expanded at compile time instead of pre-processor time.
Then without the template<> you'd have
const size_t S<U>::size = sizeof(U);
And that's not a valid syntax, because you're doing a template specialization
Related
It's not new that IntelliSense often lags behind C++ development.
For instance, the code below is valid in C++20, using the new Template String-Literal Operator feature.
template<typename C, size_t Size>
struct StrWrapper
{
std::array<C, Size> m_buf;
consteval StrWrapper(const C(&str)[Size]) : m_buf{}
{
std::copy(str, str + Size, m_buf.begin());
}
};
template <StrWrapper c>
consteval auto operator ""_wrap()
{
return c;
}
"hello world"_wrap;
But IntelliSense will report these errors:
E2500 a literal operator template must have a template parameter list equivalent to '<char ...>'
E2486 user-defined literal operator not found
I have found others who have the same problem, there are two reports on Developer Community, the earliest one is from Jan 2021, it's been nearly two years since.
It looks like Microsoft didn't want to solve the issue since this feature is somewhat not used often, and they're still struggling with the modules.
Is there any way to work around this? I've searched for a way to disable specific errors in IntelliSense but there seems to be none. Actually, there is one but it wouldn't help in this case since every single string that uses ""_wrap would have to be in the __INTELLISENSE__ preprocessor block.
Depending on how you use the return value of your operator afterwards, you might satisfy IntelliSense by providing a dummy implementation just for IntelliSense:
template <typename C, size_t Size>
struct StrWrapper
{
std::array<C, Size> m_buf;
consteval StrWrapper(const C (&str)[Size])
: m_buf{}
{
std::copy(str, str + Size, m_buf.begin());
}
};
#ifndef __INTELLISENSE__
template <StrWrapper c>
consteval auto operator""_wrap()
{
return c;
}
#else
// Dummy, only seen by IntelliSense.
consteval auto operator""_wrap(const char*, size_t)
{
return StrWrapper("");
}
#endif
void foo()
{
constexpr auto t = "hello world"_wrap;
}
In C++ sizeof is somewhat unique in that it's legal to write this:
int x;
sizeof(x); // a variable
As well as simply:
sizeof(int); // a type
(There's a third even weirder variant I'd rather ignore for now though, with no parenthesis needed, since I'm pretty certain that is impossible to emulate)
I'd like to be able to replicate this behaviour myself. To motivate it I've got an example bitsof operator.
#include <climits>
template <typename T>
struct bits_traits {
enum { value = sizeof(T) * CHAR_BIT };
};
struct int_12_bit {
enum { bits = 12 };
// Let's pretent this has a bunch of code for looking and feeling like a 12bit int in a helpful and portable way
};
template <>
struct bits_traits<int_12_bit> {
enum { value = int_12_bit::bits };
};
#define bitsof(x) bits_traits<x>::value
int main() {
using std::size_t;
size_t b = bitsof(int);
size_t a = bitsof(int_12_bit);
int_12_bit x;
size_t c = bitsof(x); // <-- Not cool
}
Clearly I could have written the whole thing in terms of a macro, using sizeof, e.g.
#define bitsof(x) (sizeof(x) * CHAR_BIT)
But then I lose the ability to "specialise" it.
And equally I could write size_t c = bitsof(decltype(x)). However what I'm asking here is for a way of emulating that behaviour in my own code without having to settle for a workaround. How can I write a bitsof that looks and feels like sizeof, but specialises like traits? Do I just have to accept that sizeof is a bit special and live with it?
I initially played with a few ideas:
Perhaps decltype works like sizeof, e.g. decltype(0) and decltype(int) are synonymous. No luck there though.
Maybe we could do something with pointer/reference template parameters. I couldn't see a way of getting deduction to work properly for that case though, and it would impose additional constraints on what variables we could use bitsof with.
Maybe some crazy SFINAE with a combination of templates and macros, but I can't see a way of making that happen, it's always just a syntax error.
Possibly something to workaround the limitations of one of the above using GCC's statement-expr extension.
As there's an easy workaround with decltype and more of a learning experiment I'm open to ideas using anything available in any C++ released compiler targeting any past, present or future standard.
You can do something like this:
#include <type_traits>
#define bitsof(k) decltype(bitsof_left+(k)+bitsof_right)
template <class K>
struct bits_traits { /* whatever you want here */ };
struct bitsof_left_t {
template <class T>
bits_traits<T> operator+(const T&);
} bitsof_left;
struct bitsof_right_t {
template <class T>
friend T operator+(const T&, bitsof_right_t);
bitsof_right_t operator+();
template <class T>
operator T() const;
} bitsof_right;
int main()
{
using foo = bitsof(42);
using bar = bitsof(int);
static_assert(std::is_same<foo, bits_traits<int>>::value);
static_assert(std::is_same<bar, bits_traits<int>>::value);
}
It works like this.
a + (42) + b is parsed as (a + (42)) + b), then overloaded binary operator+ at either side kicks in. In my example the operators are only declared, not defined, but since it's unevaluated context, it doesn't matter.
a + (int) + b is parsed as a + ((int) (+ b)). Here we employ the overloaded unary + at the right side, then overloaded cast operator, then overloaded binary + at the left side.
Its hard and probably impossible, mainly because you can only pass compile-time constants as template values to templates, hence your last example with the int_12_bit x; will never be able to be a template value (and types can't be passed as parameters, of course). I played around a bit with decltype, declval and different templates, but I simply could not get it to take in types and (non-constand expression) values with a single "call". It's really unfortunate decltype doesn't accept types, I wonder why the committee choose to only accept expressions.
Since you mentioned gcc-extensions, there is an extension which can make it work, __typeof__.
I personally have never used this extension, but it seems like it works similar to decltype but it also accepts types directly.
This snipped compiles under gcc x86-64 8.3 for me:
template<typename T>
struct bits_trait;
template<>
struct bits_trait<int>{};
void f() {
int x;
bits_trait<__typeof__(x)>();
bits_trait<__typeof__(int)>();
}
But this will only compile under gcc.
Edit: Clang seems to support it as well, no luck with MSVC though.
Not considering macros and without decltype, it is simply not possible because of the language syntax.
However you can get pretty damn close:
template <class T>
constexpr auto bitsof(T) { return sizeof(T) * CHAR_BIT; }
template <>
constexpr auto bitsof(int_12_bit) { return 12; }
template <class T>
constexpr auto bitsof() { return sizeof(T) * CHAR_BIT; }
template <>
constexpr auto bitsof<int_12_bit>() { return 12; }
auto test()
{
constexpr int a{};
constexpr int_12_bit x{};
static_assert(bitsof(a) == 32);
static_assert(bitsof(x) == 12);
static_assert(bitsof<int>() == 32);
static_assert(bitsof<int_12_bit>() == 12);
}
Aside from the slightly different syntax (but c'mon it's so close it shouldn't really matter) the biggest difference to the sizeof is that the arguments are not in an unevaluated context. So bitsof(foo()) will call foo(). And bitsof(a) is UB if a is uninitialized.
Building upon the quite magical answer from n.m., with just tiny bit of massage, it seems it is possible to have bitsof mimic sizeof.
#include <climits>
#include <iostream>
#include <type_traits>
template <typename T>
struct bits_traits {
enum { value = sizeof(T) * CHAR_BIT };
};
struct int_12_bit {
enum { bits = 12 };
};
template <>
struct bits_traits<int_12_bit> {
enum { value = int_12_bit::bits };
};
#define bits_traits_of(k) decltype(bits_traits_of_left+(k)+bits_traits_of_right)
struct bits_traits_of_left_t {
template <class T>
bits_traits<T> operator+(const T&);
} bits_traits_of_left;
struct bits_traits_of_right_t {
template <class T>
friend T operator+(const T&, bits_traits_of_right_t);
bits_traits_of_right_t operator+();
template <class T>
operator T() const;
} bits_traits_of_right;
#define bitsof(x) bits_traits_of(x)::value
int main() {
using std::size_t;
size_t a = bitsof(int);
size_t b = bitsof(int_12_bit);
std::cout <<"a="<< a <<", b="<< b << std::endl;
int_12_bit x;
size_t c = bitsof(x);
std::cout <<"c="<< c << std::endl;
}
The only thing that I changed, other than adding in definitions for bits_traits, is to redefine bitsof so that it returns the bits_traits::value rather than the bits_traits type.
$ ./a.out
a=32, b=12
c=12
I'm just writing this up to verify that it can work. All credits should go to n.m.'s answer.
I have a few lines that compile well on my system but don't compile on a colleagues system. That's why I would like to ask what the go-to solution for the problem would look like. I have to deal with a enum that implicitly defines how much space I have to provide for an std::array. Other parts of the code also make use of FooSize being static. (Optimization)
My current implementation looks like this
enum class FooType
{
ShortFoo,
LongFoo
};
// defined in a different file
template <FooType FType>
class FooContainer
{
public:
static const unsigned int FooSize {(FType == FooType::ShortFoo) ? 32 : 64 };
std::array<float, FooSize> fooArray;
};
The code seems to create issues on older llvm / clang compilers. 32 and 64 are actually provided via pre processor defines. I could just skip the FooType and use the size as an template argument but I would like to know what the most reliable method of initializing FooSize would be.
Your code seems correct to me and compile without problems with my olds g++ (4.9.2) and clang++ (3.5).
But, according the error message, could be that your compiler doesn't correctly support the C++11 declaration/initialization of static data members
I suggest you to try in the following way
template <FooType FType>
class FooContainer
{
public:
static const unsigned int FooSize;
std::array<float, FooSize> fooArray;
};
template <FooType FType>
int unsigned const FooContainer<FType>::FooSize
= ((FType == FooType::ShortFoo) ? 32 : 64);
or (I suppose is better)
template <FooType FType>
class FooContainer
{
public:
static const unsigned int FooSize {(FType == FooType::ShortFoo) ? 32 : 64 };
std::array<float, FooSize> fooArray;
};
template <FooType FType>
int unsigned const FooContainer<FType>::FooSize;
You can also try defining FooSize as constexpr instead of const.
Another solution could be transform FooSize in a template parameter
template <FooType FType,
std::size_t FooSize = (FType == FooType::ShortFoo) ? 32 : 64 >
class FooContainer
{
public:
std::array<float, FooSize> fooArray;
};
I want a static array in a templated function whose length depends on the type with which the function is specialized. My first attempt was:
Header:
template<typename T>
struct Length {
const static size_t len;
};
template<typename T>
void func(){
static T vars[Length<T>::len]; // len not const. according to compiler!
// ...
}
Source file:
template<> const size_t Length<double>::len = 2;
template<> const size_t Length<float>::len = 1;
// ...
However, g++ does not compile this and complains
error: storage size of ‘vars’ isn’t constant
So, what exactly is the problem here? I know that the size of a fixed-length array needs to be a constant and known on compile time, but that seems to be the case here.
When I write
const size_t len = 2;
void func(){
static double vars[len];
}
it compiles without problems.
Question:
What is wrong with the code and which alternatives are there for achieving the desired behavior? I would not like to allocate the memory during runtime...
For a const variable to be considered a compile-time constant (formally, a constant expression), its value must be available at point of use. Which means the specialised definitions would have to go to the header file.
If done as just specialisations of the member, as you did, I believe that would give you a multiple-definition error. You should be fine with specialising the entire class template, and keeping the static member definition inline:
template<typename T>
struct Length;
template <>
struct Length<double>
{
static const size_t len = 2;
};
As a side note, your program was originally invalid: an explicit specialisation must be declared before use. Which means you'd have to at least declare the specialisation of len in the header (or everywhere you intended to use it).
The following code compiles fine for me with g++ 4.6.3 and produces the output
2
1
array.cpp:
#include <cstddef>
#include <iostream>
template<typename T>
struct Length {
const static size_t len;
};
template<typename T>
void func(){
static T vars[Length<T>::len];
std::cout << (sizeof(vars) / sizeof(*vars)) << std::endl;
}
template<> const size_t Length<double>::len = 2;
template<> const size_t Length<float>::len = 1;
int main(){
func<double>();
func<float>();
}
$ make array
g++ array.cpp -o array
$ ./array
2
1
I'm trying to figure our how I can get some code to compile that will determine the size of T's return value, where T is a function prototype, in my function template.
template<typename T>
void functionReturnLength()
{
long lReturnTypeSize = boost::mpl::eval_if<
boost::is_void<boost::function_types::result_type<T>::type>::value,
boost::mpl::long_<0>,
boost::mpl::long_<boost::mpl::sizeof_<boost::function_types::result_type<T>::type>::value>>::value;
}
However it still does not compile because sizeof(void) is not a valid operation - even though I am trying to construct an if-statement that will return a size of 0 if the type is void. I'm fairly new to BOOST MPL, so, while I have been browsing the documentation for some time, I am not sure how I could apply other ifs like if_ or apply_if, and if these would even work.
Thanks.
You can use your own metafunction
template<typename T>
struct get_size { static const size_t value = sizeof(T); };
template<>
struct get_size<void> { static const size_t value = 0; };