understanding of non-type template parameters - c++

I have problem understanding the following paragraph Per C++11 Standard N3485 Section 14.1.7.
I think it is more important to understand the rationale instead of memorizing the facts.
A non-type template-parameter shall not be declared to have floating point, class, or void type.
[ Example:
template<double d> class X; // error
template<double* pd> class Y; // OK
template<double& rd> class Z; // OK
—end example ]
I have some questions regarding this rule:
Is there a reason that why floating point type cannot be used as template parameter? What is the rationale behind that? I know this is true before C++11 and it seems also true for C++11 standard.
Why it is OK to use pointer or reference to floating point types as non-template parameters, but not raw floating point type? What is the big difference here?
Thank you for your help.

Is there a reason that why floating point type cannot be used as template parameter? What is the rationale behind that?
While I cannot give the ultimate reason, I can definitely imagine there would be problems with specializing a template that accepts a floating pointer value as a parameter.
Equality comparisons between floating point numbers is tricky (in the sense that it sometimes gives unexpected results), and when matching specializations, the compiler would have to perform an equality check between the argument provided and the value for which a template is being specialized.
Another similar issue is determining whether two instances of the same class templates are actually the same type:
template<double D>
struct X
{
// ...
};
int main()
{
X<3.0> x;
X<some_constant_expression()> y;
}
Are x and y instances of the same class? To decide this, an equality check has to be performed between 3.0 and some_constant_expression().
Why it is OK to use pointer or reference to floating point types as non-template parameters, but not raw floating point type?
With respect to the above scenario, the answer concerning pointers is simple: pointers are integral values, and comparison between pointers is well defined.
Concerning references, evidence shows that they are in fact treated like pointers:
#include <type_traits>
double a = 0.0;
double b = 0.0;
template<double& D>
struct X : std::false_type { };
template<>
struct X<a> : std::true_type { }
int main()
{
static_assert(X<a>::value, "!"); // Does not fire
static_assert(X<b>::value, "!"); // Fires
}
Also, per paragraph 14.4/1 of the C++11 Standard:
Two template-ids refer to the same class or function if
— [...]
— their corresponding non-type template arguments of integral or enumeration type have identical values and
— [...]
— their corresponding non-type template-arguments of reference type refer to the same external object
or function and
— [...]
[ Example:
template<class E, int size> class buffer { / ... / };
buffer<char,2*512> x;
buffer<char,1024> y;
declares x and y to be of the same type, and [...]
The above shows that determining whether two different instances of the same class template are actually the same class requires an equality check between the constant expressions used as template arguments.

To figure this out, consider that integral types and pointers always have a one-to-one relationship with their literal representation.
But then let's consider a non-type template Foo<float>.
Let's say it's specialized for a non-binary-representable numver like 0.1: Foo<0.1> and let's say the compiler decorates the symbol name based on the specialization and you wind up with something like Foo_3DCCCCCC because the compiler is using "round down" for an IEEE 754 32 bit rep.
But then let's say that the user of this code is compiling is such a way that the compiler chooses "round to positive infinity" instead. Then then specialization's name is instead Foo_3DCCCCCD which is a completely different function from the one that was previously specialized in another translation unit.
Unless you start making up a wide variety of rules to handle all these sorts of things (what about NaN, infinity, and other not-a-normal-numbers?) then you open yourself to mismatches and all sorts of possible problems.

Related

Are there any pitfalls converting compile-time constant members from enum to static constexpr?

Consider the following two pieces of code:
template <int X>
struct Foo
{
enum
{
x = X
};
};
vs
template <int X>
struct Foo
{
static constexpr int x = X;
};
(The former is a frequent pattern in a library I want to modernize to C++17.)
I have looked through many questions/answers on here covering the differences between the enum vs static constexpr variants, but with C++17 having changed the behavior of static constexpr (by making such variables implicitly inline) many of them are outdated (example). It's not perfectly clear to me what the remaining differences are and if I am missing something important.
Is going from the first snippet above to the second a safe transformation? Or are there any potential breakages or changes in behavior affecting user code that I should be aware of?
It is incorrect to assume that the underlying type of the unscoped enum in the original struct is actually an int. As declared, the following applies:
Declares an unscoped enumeration type whose underlying type is not fixed (in this case, the underlying type is an implementation-defined integral type that can represent all enumerator values; this type is not larger than int unless the value of an enumerator cannot fit in an int or unsigned int
see: https://en.cppreference.com/w/cpp/language/enum
The type of the enum is actually undefined. Unscoped enums implicitly convert to integral types (and from there, can be converted to floating-point types) in calling code. It could also mean different compilers will create a different underlying type for the enum. By default; these two types are not interchangeable.
It should be safe, you just have limited what language standards allow your code to compile. If you don't want to restrict the code to only working in C++17, then you can use
template <int X>
struct Foo
{
static constexpr int x;
};
template <int X>
constexpr int Foo<X>::x = X;
This will allow the code to work with C++11. C++17 has an explicit exemption for this construct as it would break a lot of code if all of the sudden the out of line definitions cause an ODR violation.

How does this implementation of std::is_class work?

I'm trying to understand the implementation of std::is_class. I've copied some possible implementations and compiled them, hoping to figure out how they work. That done, I find that all the computations are done during compilation (as I should have figured out sooner, looking back), so gdb can give me no more detail on what exactly is going on.
The implementation I'm struggling to understand is this one:
template<class T, T v>
struct integral_constant{
static constexpr T value = v;
typedef T value_type;
typedef integral_constant type;
constexpr operator value_type() const noexcept {
return value;
}
};
namespace detail {
template <class T> char test(int T::*); //this line
struct two{
char c[2];
};
template <class T> two test(...); //this line
}
//Not concerned about the is_union<T> implementation right now
template <class T>
struct is_class : std::integral_constant<bool, sizeof(detail::test<T>(0))==1
&& !std::is_union<T>::value> {};
I'm having trouble with the two commented lines. This first line:
template<class T> char test(int T::*);
What does the T::* mean? Also, is this not a function declaration? It looks like one, yet this compiles without defining a function body.
The second line I want to understand is:
template<class T> two test(...);
Once again, is this not a function declaration with no body ever defined? Also what does the ellipsis mean in this context? I thought an ellipsis as a function argument required one defined argument before the ...?
I would like to understand what this code is doing. I know I can just use the already implemented functions from the standard library, but I want to understand how they work.
References:
std::is_class
std::integral_constant
What you are looking at is some programming technologie called "SFINAE" which stands for "Substitution failure is not an error". The basic idea is this:
namespace detail {
template <class T> char test(int T::*); //this line
struct two{
char c[2];
};
template <class T> two test(...); //this line
}
This namespace provides 2 overloads for test(). Both are templates, resolved at compile time. The first one takes a int T::* as argument. It is called a Member-Pointer and is a pointer to an int, but to an int thats a member of the class T. This is only a valid expression, if T is a class.
The second one is taking any number of arguments, which is valid in any case.
So how is it used?
sizeof(detail::test<T>(0))==1
Ok, we pass the function a 0 - this can be a pointer and especially a member-pointer - no information gained which overload to use from this.
So if T is a class, then we could use both the T::* and the ... overload here - and since the T::* overload is the more specific one here, it is used.
But if T is not a class, then we cant have something like T::* and the overload is ill-formed. But its a failure that happened during template-parameter substitution. And since "substitution failures are not an error" the compiler will silently ignore this overload.
Afterwards is the sizeof() applied. Noticed the different return types? So depending on T the compiler chooses the right overload and therefore the right return type, resulting in a size of either sizeof(char) or sizeof(char[2]).
And finally, since we only use the size of this function and never actually call it, we dont need an implementation.
Part of what is confusing you, which isn't explained by the other answers so far, is that the test functions are never actually called. The fact they have no definitions doesn't matter if you don't call them. As you realised, the whole thing happens at compile time, without running any code.
The expression sizeof(detail::test<T>(0)) uses the sizeof operator on a function call expression. The operand of sizeof is an unevaluated context, which means that the compiler doesn't actually execute that code (i.e. evaluate it to determine the result). It isn't necessary to call that function in order to know the sizeof what the result would be if you called it. To know the size of the result the compiler only needs to see the declarations of the various test functions (to know their return types) and then to perform overload resolution to see which one would be called, and so to find what the sizeof the result would be.
The rest of the puzzle is that the unevaluated function call detail::test<T>(0) determines whether T can be used to form a pointer-to-member type int T::*, which is only possible if T is a class type (because non-classes can't have members, and so can't have pointers to their members). If T is a class then the first test overload can be called, otherwise the second overload gets called. The second overload uses a printf-style ... parameter list, meaning it accepts anything, but is also considered a worse match than any other viable function (otherwise functions using ... would be too "greedy" and get called all the time, even if there's a more specific function t hat matches the arguments exactly). In this code the ... function is a fallback for "if nothing else matches, call this function", so if T isn't a class type the fallback is used.
It doesn't matter if the class type really has a member variable of type int, it is valid to form the type int T::* anyway for any class (you just couldn't make that pointer-to-member refer to any member if the type doesn't have an int member).
The std::is_class type trait is expressed through a compiler intrinsic (called __is_class on most popular compilers), and it cannot be implemented in "normal" C++.
Those manual C++ implementations of std::is_class can be used in educational purposes, but not in a real production code. Otherwise bad things might happen with forward-declared types (for which std::is_class should work correctly as well).
Here's an example that can be reproduced on any msvc x64 compiler.
Suppose I have written my own implementation of is_class:
namespace detail
{
template<typename T>
constexpr char test_my_bad_is_class_call(int T::*) { return {}; }
struct two { char _[2]; };
template<typename T>
constexpr two test_my_bad_is_class_call(...) { return {}; }
}
template<typename T>
struct my_bad_is_class
: std::bool_constant<sizeof(detail::test_my_bad_is_class_call<T>(nullptr)) == 1>
{
};
Let's try it:
class Test
{
};
static_assert(my_bad_is_class<Test>::value == true);
static_assert(my_bad_is_class<const Test>::value == true);
static_assert(my_bad_is_class<Test&>::value == false);
static_assert(my_bad_is_class<Test*>::value == false);
static_assert(my_bad_is_class<int>::value == false);
static_assert(my_bad_is_class<void>::value == false);
As long as the type T is fully defined by the moment my_bad_is_class is applied to it for the first time, everything will be okay. And the size of its member function pointer will remain what it should be:
// 8 is the default for such simple classes on msvc x64
static_assert(sizeof(void(Test::*)()) == 8);
However, things become quite "interesting" if we use our custom type trait with a forward-declared (and not yet defined) type:
class ProblemTest;
The following line implicitly requests the type int ProblemTest::* for a forward-declared class, definition of which cannot be seen by the compiler right now.
static_assert(my_bad_is_class<ProblemTest>::value == true);
This compiles, but, unexpectedly, breaks the size of a member function pointer.
It seems like the compiler attempts to "instantiate" (similarly to how templates are instantiated) the size of a pointer to ProblemTest's member function in the same moment that we request the type int ProblemTest::* within our my_bad_is_class implementation. And, currently, the compiler cannot know what it should be, thus it has no choice but to assume the largest possible size.
class ProblemTest // definition
{
};
// 24 BYTES INSTEAD OF 8, CARL!
static_assert(sizeof(void(ProblemTest::*)()) == 24);
The size of a member function pointer was trippled! And it cannot be shrunk back even after the definition of class ProblemTest has been seen by the compiler.
If you work with some third party libraries that rely on particular sizes of member function pointers on your compiler (e.g., the famous FastDelegate by Don Clugston), such unexpected size changes caused by some call to a type trait might be a real pain. Primarily because type trait invocations are not supposed to modify anything, yet, in this particular case, they do -- and this is extremely unexpected even for an experienced developer.
On the other hand, had we implemented our is_class using the __is_class intrinsic, everything would have been OK:
template<typename T>
struct my_good_is_class
: std::bool_constant<__is_class(T)>
{
};
class ProblemTest;
static_assert(my_good_is_class<ProblemTest>::value == true);
class ProblemTest
{
};
static_assert(sizeof(void(ProblemTest::*)()) == 8);
Invocation of my_good_is_class<ProblemTest> does not break any sizes in this case.
So, my advice is to rely on the compiler intrinsics when implementing your custom type traits like is_class wherever possible. That is, if you have a good reason to implement such type traits manually at all.
What does the T::* mean? Also, is this not a function declaration? It looks like one, yet this compiles without defining a function body.
The int T::* is a pointer to member object. It can be used as follows:
struct T { int x; }
int main() {
int T::* ptr = &T::x;
T a {123};
a.*ptr = 0;
}
Once again, is this not a function declaration with no body ever defined? Also what does the ellipsis mean in this context?
In the other line:
template<class T> two test(...);
the ellipsis is a C construct to define that a function takes any number of arguments.
I would like to understand what this code is doing.
Basically it's checking if a specific type is a struct or a class by checking if 0 can be interpreted as a member pointer (in which case T is a class type).
Specifically, in this code:
namespace detail {
template <class T> char test(int T::*);
struct two{
char c[2];
};
template <class T> two test(...);
}
you have two overloads:
one that is matched only when a T is a class type (in which case this one is the best match and "wins" over the second one)
on that is matched every time
In the first the sizeof the result yields 1 (the return type of the function is char), the other yields 2 (a struct containing 2 chars).
The boolean value checked is then:
sizeof(detail::test<T>(0)) == 1 && !std::is_union<T>::value
which means: return true only if the integral constant 0 can be interpreted as a pointer to member of type T (in which case it's a class type), but it's not a union (which is also a possible class type).
Test is an overloaded function that either takes a pointer to member in T or anything. C++ requires that the best match be used. So if T is a class type it can have a member in it...then that version is selected and the size of its return is 1. If T is not a class type then T::* make zero sense so that version of the function is filtered out by SFINAE and won't be there. The anything version is used and it's return type size is not 1. Thus checking the size of the return of calling that function results in a decision whether the type might have members...only thing left is making sure it's not a union to decide if it's a class or not.
Here is standard wording:
[expr.sizeof]:
The sizeof operator yields the number of bytes occupied by a non-potentially-overlapping object of the type of its operand.
The operand is either an expression, which is an unevaluated operand
([expr.prop])......
2. [expr.prop]:
In some contexts, unevaluated operands appear ([expr.prim.req], [expr.typeid], [expr.sizeof], [expr.unary.noexcept], [dcl.type.simple], [temp]).
An unevaluated operand is not evaluated.
3. [temp.fct.spec]:
[Note: Type deduction may fail for the following reasons:
...
(11.7) Attempting to create “pointer to member of T” when T is not a class type.
[ Example:
template <class T> int f(int T::*);
int i = f<int>(0);
— end example
]
As above shows, it is well-defined in standard :-)
4. [dcl.meaning]:
[Example:
struct X {
void f(int);
int a;
};
struct Y;
int X::* pmi = &X::a;
void (X::* pmf)(int) = &X::f;
double X::* pmd;
char Y::* pmc;
declares pmi, pmf, pmd and pmc to be a pointer to a member of X of type int, a pointer to a member of X of type void(int), a pointer to a member ofX of type double and a pointer to a member of Y of type char respectively.The declaration of pmd is well-formed even though X has no members of type double. Similarly, the declaration of pmc is well-formed even though Y is an incomplete type.

Why is digits10 for reference to integer type 0?

The following code:
#include <iostream>
#include <limits>
#include <cstdint>
int main()
{
std::cout << std::numeric_limits<std::uint64_t>::digits10 << "\n"
<< std::numeric_limits<std::uint64_t&>::digits10 << "\n";
}
outputs
19
0
I would expect std::uint64_t& to have same value as std::uint64_t: Is there a reason for this discrepancy?
18.3.2.1/2:
Specializations shall be provided for each arithmetic type, both
floating point and integer, including bool. The member is_specialized
shall be true for all such specializations of numeric_limits.
So we know that specializations will exist for for these non-reference types. Then 18.3.2.3/1:
The default numeric_limits template shall have all members, but
with 0 or false values.
I suspect it was done this way because you can always static_assert on is_specialized to force a compile error, but there could possibly be some template application where 0 would be an ok default value for one or more of the limits. If you want to be able to test references just run it through std::remove_reference.
I'm not sure if it's legal to specialize numeric_limits for your own types. The standard in 18.3.2.1/4 says:
Non-arithmetic standard types, such as complex (26.4.2), shall not
have specializations.
I personally read this qute as "the standard will not provide specializations for non-arithmetic standard libray types, but it's perfectly legal to specialize for a user type". You could just as easily read this as totally forbidding any non-provided specializations.
The type uint64& is a reference, so it's not one of the arithmetic types that the numeric_limits template should be used for.
For any other types than the arithmetic types defined, the default definition is used which contains:
static const int digits10 = 0;
Reference: http://www.cplusplus.com/reference/limits/numeric_limits/
std::numeric_limits is expected to be specialized for fundamental arithmetic data types (integers and floating points). A reference -of any kind- does not belong to them and that's why the non-specialized template is chosen, which has all its members to false (0).
I would expect std::uint64_t& to have same value as std::uint64_t: Is
there a reason for this discrepancy?
Although std::uint64_t is an arithmetic type, std::uint64_t& is a compound type and has a different meaning. You have a few options:
Always consider the base type by removing the reference, pointer or both.
static_assert( is_arithmetic<> )
check std::numeric_limits<T>::is_specialized

Need help to understand the purpose of a following class

The friend of mine send me an interesting task:
template<typename T>
class TestT
{
public:
typedef char ONE;
typedef struct { char a[2]; } TWO;
template<typename C>
static ONE test(int C::*);
template<typename C>
static TWO test(...);
public:
enum{ Yes = sizeof(TestT<T>::template test<T>(0)) == 1 };
enum{ No = !Yes };
};
I can't compile this code with VS2013. With GCC 4.9.0 it compiles. But I can't understand what it does.
The points of interest for me:
How it can work if functions have a declaration only but no definition?
What the TestT<T>::template test<T>(0) is? It looks like a function call.
What is this ::template means?
What's purpose of class above?
How is used principle called?
int C::* is a pointer to a int member, right?
It does not actually call the functions, it just looks at what the sizeof of the return type would be.
It is a function call. See below.
The template is necessary because of the dependent type problem.
It tests if there can be a pointer to a data member of the type parameter. This is true for class types only (e.g. for std::string but not for int). You can find code like this for example here which includes something very similar to your example - under the name of is_class.
SFINAE, which stands for "Substitution Failure Is Not An Error". The reason for this name becomes obvious once you realize that the substitution of C for int will fail and thus simply cause one of the function overloads to not exist (instead of causing a compiler error and aborting compilation).
Yes, it is a pointer that points to an int inside of an object of type C.
That's too many questions for a single question, but nevertheless:
sizeof doesn't evaluate its operand, it only determines the type. That doesn't require definitions for any functions called by the operand - the type is determined from the declaration.
Yes, that's a function call. If the class and function weren't templates, it would look like TestT::test(0).
template is needed because the meaning of the name test depends on the class template parameter(s), as described in Where and why do I have to put the "template" and "typename" keywords?.
It defines a constant Yes which is one if T is a class type and zero otherwise. Also a constant No with the logically inverted value.
It looks like it's intended for use in SFINAE, to allow templates to be partially specialised for class and non-class types. Since C++11, we can use standard traits like std::is_class for this purpose.
Yes, if C is a class type. Otherwise, it's a type error, so the overload taking that type is ignored, leaving just the second overload. Thus, the return type of test is ONE (with size one) if C is a class type, and TWO (with size two) otherwise; so the test for sizeof(...) == 1 distinguishes between class and non-class types.

Why is C++ numeric_limits<enum_type>::max() == 0?

Here's a bit of code that might seem like it would work:
#include <cassert>
#include <limits>
enum test { A = 1 };
int main()
{
int max = std::numeric_limits<test>::max();
assert(max > 0);
}
But it fails under both GCC (4.6.2) and clang (2.9) on Linux: max() for enum types is in fact zero! And this remains true even if you use the C++11 enum type specifier to explcitly say what type you want your enum to have.
Why is this? And as for the C++11 behavior, is it something explcitly called for? I could find no mention of it in N2347, the paper on Strongly Typed Enums.
std::numeric_limits is specialized in the Standard Library "for each arithmetic type, both floating point and integer, including bool" (§18.3.2.1/2).
Your enumeration test is not one of these types, so the primary template is used. Its behavior is specified by §18.3.2.3/1: "The default numeric_limits<T> template shall have all members, but with 0 or false values."
If you want to know the traits of the underlying type of test, you can use underlying_type:
std::numeric_limits<std::underlying_type<test>::type>::max()
Alternatively, you can specialize numeric_limits for test and have it return the values you want. This is not a particularly good idea, though.
For non-specialized versions of the template, max returns T(). You have not written a numeric_limits specialization for your test type, so you get the default implementation.
The numeric_limits<T> is a regular class template, it is not connected to the compiler in any special way as to find out about user-defined enum types. If you look at the <limits> file, it has the default template definition that returns zeros for everything, and a whole bunch of type-specific specifications for the individual types, returning the right constants.
You can "plug in" your enum into numeric_limits by providing a specification of numeric_limits<test> by yourself. You can copy the one for int from the <limits>, and modify it to suit your needs.
From the C++11 draft:
In 18.3.2.1, about numeric_limits:
Non-arithmetic standard types, such as complex (26.4.2), shall not have specializations.
And an enum is not an arithmetic standard type.
Then, in the non-specialized template:
template<class T> class numeric_limits {
public:
[...]
static constexpr bool is_specialized = false;
static constexpr T max() noexcept { return T(); }
};
That is, the non-specialized max() function returns the default initialized value for that type, that is 0.