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
Related
#include <iostream>
template<typename T>
const T unit{ 1 };
template<typename T>
struct data
{
typename T::value_type val;
};
struct decimal_def
{
using value_type = int;
static const value_type unit_val() {
return 10;
}
};
template<typename T>
const data<T> unit<data<T>> { T::unit_val() };
auto decimal_data_unit = unit<data<decimal_def>>;
int main(){
std::cout << decimal_data_unit.val << std::endl;
return 0;
}
exoect: 10
gcc: 10
msvc: 0
I think the reason of the difference between gcc and msvc is that compilers optimize unit_val() differently.
Dynamic initialization of templated non-local variables is always unordered, even relative to other variables defined in the same translation unit (in part because templated variables might also be defined in other translation units). In this simple case, you can avoid the issue by using constant initialization instead: add constexpr to decimal_def::unit_val to enable that, and to the variable template definitions for clarity.
I am attempting to create the equivalent of the Visual Studio _countof macro using C++ templates. The following are my proposed definitions:
template<typename T, size_t N>
inline constexpr size_t countof(T const (&array)[N]) {
return N;
}
template<typename T, typename U, size_t N>
inline constexpr size_t countof(T const (U::&array)[N]) {
return N;
}
The second declaration above was an attempt to fix the following code, which generates a compile-time error in g++ 9 with the message: "error: invalid use of non-static data member ‘foo::bar’":
struct foo {
int const bar[4];
static_assert(countof(bar) == 4);
};
However, when I add the second definition, and change the assertion to use foo::bar, g++ generates the error: "error: ‘template constexpr const size_t countof’ conflicts with a previous declaration".
I can change the code to use pointer-to-member (instead of reference to member), but that seems like it should be unnecessary. Does anyone know of a way to make a version of countof that only compiles when passed an array, and works in a reasonable way for both free and member variable arrays?
The problem is the usage of bar is invalid in static_assert(countof(bar) == 4);, you need an instance of foo and get the member array bar to pass to countof.
I can change the code to use pointer-to-member (instead of reference to member), but that seems like it should be unnecessary.
You can change the code to use pointer-to-member. e.g.
template<typename T, typename U, size_t N>
inline constexpr size_t countof(T const (U::*array)[N]) {
return N;
}
then
static_assert(countof(&foo::bar) == 4);
LIVE
Or change countof to specify the type instead of passing the array to it.
template<typename T>
struct count_of{};
template<typename T, size_t N>
struct count_of<T const [N]> {
constexpr static size_t value = N;
};
template<typename T>
inline constexpr size_t countof() {
return count_of<T>::value;
}
then
static_assert(countof<decltype(foo::bar)>() == 4);
LIVE
I couldn't figure out a way to do this without macros, but this post provides a way to get both type-safety (ensuring the argument passed to countof is an array) and supports both free and member arrays. The resulting code is:
template<typename T, size_t N>
char (&COUNTOF_REQUIRES_ARRAY_ARGUMENT(T (&array)[N]))[N];
#define countof(x) sizeof(COUNTOF_REQUIRES_ARRAY_ARGUMENT(x))
Why doesn't the below code compile? It says that S has to be const as one of the major errors.
template <int S>
class Array
{
int size;
int items [S];
public:
Array(void) :size(S){}
};
void main()
{
int S= 30;
Array <5+S> array;
}
Nontype template parameters must be constexpr, i.e., they have to be known at compile time. Hence, S must be declared as constexpr int.
I searched online for the similar warnings; most people run into the similar warning because they defined the inline function in another translation unit (e.g. the cpp file).
But I put the definition within the same unit.
#include <iostream>
#include <string.h>
template <typename T>
struct MagicInt { constexpr static const char* str(); };
template <>
struct MagicInt<int8_t> {
constexpr static const char* str() {
return "123";
}
};
template<typename T>
inline char* foo(char *buf, T t) {
if (t < T(5)) return (char*)memcpy(buf, MagicInt<T>::str(), strlen(MagicInt<T>::str())) + strlen(MagicInt<T>::str());
return buf;
}
int main() {
char buffer[1024];
buffer[0] = '1';
buffer[1] = '0';
std::cout << foo(buffer, uint16_t(123)) << std::endl;
}
$ g++ -Wall iwarn.cpp -o iwarn -lrt -std=c++0x -O3
iwarn.cpp:5:48: warning: inline function ‘static constexpr const char* MagicInt<T>::str() [with T = short unsigned int]’ used but never defined [enabled by default]
struct MagicInt { constexpr static const char* str(); };
^
My gcc version:
$ g++ --version
g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-11)
It looks to me that gcc was looking for the definition of MagicInt<uint16_t>::str().
I can think of 3 ways to fix it:
1. defining MagicInt<uint16_t>::str(), tho it will never be called
2. providing a default implementation (in addition to the partial template specialization)
3. suppress the warning?
Which one is cleaner? Which one generates less code (probably #4 -- ignoring the warning)?
Yes, the compiler is looking for the definition of MagicInt<uint16_t>::str().
The reason it is doing so is
The call of foo(buffer, uint16_t(123)) (with result streamed to std::cout is an exact match for the templated foo<uint16_t>(). Since you have explicitly forced the second argument of foo() to be of type uint16_t that should not be surprising.
To instantiate foo<uint16_t>() (for which the template parameter T is uint16_t) the compiler must instantiate MagicInt<uint16_t>. If it does not, it cannot compile the statement
return (char*)memcpy(buf, MagicInt<T>::str(), strlen(MagicInt<T>::str())) + strlen(MagicInt<T>::str());
The fact that main() has passed uint16_t(123) is irrelevant. The instantiation of the template above is based on the type (T) of the function parameter, not the value passed. The exact same logic as above would be used if main() instead did;
int main()
{
char buffer[1024];
buffer[0] = '1';
buffer[1] = '0';
uint16_t v;
std::cin >> v;
std::cout << foo(buffer, v) << std::endl;
}
because it is the type of v that matters in instantiating the template, not the value passed (regardless of whether that value is hard-coded - expressed as a literal - or determined at runtime (like I have done here)).
The simple ways to fix the problem are to define the functions being instantiated. Either of your options 1 and 2 will do that job.
Option 3 (assuming it is possible - I haven't checked) will suppress the warning but - in a typical "compile-link" toolchain, the typical result will be a linker error - because MagicInt<uint16_t>::str() will still not be defined.
The real solution is to adjust your expectations. The definition
template<typename T>
inline char* foo(char *buf, T t) {
if (t < T(5)) return (char*)memcpy(buf, MagicInt<T>::str(), strlen(MagicInt<T>::str())) + strlen(MagicInt<T>::str());
return buf;
}
is required to treated as if the value of t is unknown at compile time. In other words, the compiler is not required to go through a sequence of detecting that the value passed is uint16_t<123), determining that t < T(5) is always false, and then not instantiating MagicInt<uint16_t>.
The problem is that you have specialized the template for int8_t but you are using uint16_t. The uint16_t version of MagicInt was never defined.
If your argument is const expression, you might provide it in template parameter:
template<typename T, T t>
std::enable_if_t<(t < T(5)), char*>
char* foo(char *buf) {
return (char*)memcpy(buf, MagicInt<T>::str(), strlen(MagicInt<T>::str()))
+ strlen(MagicInt<T>::str());
}
template<typename T, T t>
std::enable_if_t<!(t < T(5)), char*>
char* foo(char *buf) {
return buf;
}
or in C++17
template<typename T, T t>
char*
char* foo(char *buf) {
if constexpr (t < T(5)) {
return (char*)memcpy(buf, MagicInt<T>::str(), strlen(MagicInt<T>::str()))
+ strlen(MagicInt<T>::str());
} else {
return buf;
}
}
Then MagicInt<std::uint16_t>::str() wont be instantiated.
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