Given a class definition in C++
class A
{
public:
//methods definition
....
private:
int i;
char *str;
....
}
Is it possible to calculate the offset of a class member at compile time using C++ template meta-programming? The class is not POD, and can have virtual methods, primitive and object data member.
Based on Matthieu M.'s answer but shorter and with no macros:
template<typename T, typename U> constexpr size_t offsetOf(U T::*member)
{
return (char*)&((T*)nullptr->*member) - (char*)nullptr;
}
And it's called like this:
struct X { int a, b, c, d; }
std::cout << "offset of c in X == " << offsetOf(&X::c);
Edit:
Jason Rice is correct. This will not produce an actual constant expression in C++11. It doesn't look possible given the restrictions in http://en.cppreference.com/w/cpp/language/constant_expression -- in particular no pointer difference and reinterpret_castcan be in a constant expression.
Well... in C++11, you actually can compute such offsets right with regular C++ facilities (ie, without delegating to a particular compiler intrinsic).
In action at liveworkspace:
template <typename T, typename U>
constexpr int func(T const& t, U T::* a) {
return (char const*)&t - (char const*)&(t.*a);
}
However this relies on t being a reference to a constexpr instance here, which might not be applicable to all classes. It does not forbid T from having a virtual method though, nor even a constructor, as long as it is a constexpr constructor.
Still, this is quite a hindrance. In unevaluated contexts we could actually use std::declval<T>() to simulate having an object; while having none. This poses no specific requirements on the constructor of an object, therefore. On the other hand, the values we can extract from such context are few... and they do pose issues with current compilers, too... Well, let's fake it!
In action at liveworkspace:
template <typename T, typename U>
constexpr size_t offsetof_impl(T const* t, U T::* a) {
return (char const*)t - (char const*)&(t->*a) >= 0 ?
(char const*)t - (char const*)&(t->*a) :
(char const*)&(t->*a) - (char const*)t;
}
#define offsetof(Type_, Attr_) \
offsetof_impl((Type_ const*)nullptr, &Type_::Attr_)
The only issue I foresee is with virtual inheritance, because of its runtime placement of the base object. I would be glad to be presented with other defects, if there are.
No, not in general.
The offsetof macro exists for POD (plain old data) structs, and it may be extended slightly with C++0x to standard layout structs (or other similar slight extensions). So for those restricted cases, you have a solution.
C++ offers a lot of freedom to compiler writers. I don't know of any clause that would prevent some classes from having variable offsets to members of the class -- however, I'm not sure why a compiler would do that either. ;)
Now, one approach to keep your code standards compliant, yet still have offsets, would be to stick your data into a POD (or some C++0x extension) sub-struct, on which offsetof will work, then work on that sub-struct instead of on the entire class. Or you could surrender standards compliance. The offset of your struct within your class wouldn't be known, but the offset of the member within the struct would be.
An important question to ask is "why do I want this, and do I really have a good reason"?
In the 1996 book "Inside the C++ Object Model", written by Stanley B. Lippman, one of the original C++ designer's, makes reference to Pointer-to-Member Functions in Chapter 4.4
the value returned from taking the address of a nonstatic data member is the byte value of the member's position in the class layout (plus 1). One can think of it as an incomplete value. It needs to be bound to the address of a class object before an actual instance of the member can be accessed.
While I vaguely recall that +1 from somewhere in some previous life, I've never seen or made use of this syntax before.
class t
{
public:
int i;
int j;
};
int (t::*pmf)() = &t::i;
At least according to the description, this seems to be a cool way to get "almost" the offset.
But It doesn't seem to work anymore, at least in GCC. I get a
Cannot initialize a variable of type 'int (t::*) with an rvalue of type "int t:: *'
Does anybody have any history behind what is going on here? Is something like this still possible?
Problem with the web -- obsolete books never die...
Related
My current question has been inspired by attempting to understand how std::unique_ptr<T, D> utilizes template mechanics to instantiate a template class the size of a T* when D (the deleter type) is a lambda function type, but a larger size when D is a function pointer type (since space needs to be allocated in the unique_ptr instance to store the function pointer).
Looking through the VS2015 source code, I find that std::unique_ptr derives from std::_Unique_ptr_base, which in turn declares a data member of type _Compressed_pair<class _Ty1, class _Ty2, bool = is_empty<_Ty1>::value && !is_final<_Ty1>::value>. The type _Ty1 in this latter context is the type of the deleter, D, that is the second unique_ptr template parameter noted in the previous paragraph; i.e., the motivation behind this question is that I am contrasting _Ty1 being a lambda type, vs. _Ty1 being a function pointer type. (In fact, the default value of the bool is being utilized.)
I recognize that is_empty<_Ty1>::value is true when _Ty1 is an instance of a lambda type (when the lambda has no capture variables and therefore has a 0 size); but that it is false when _Ty1 is a function pointer type.
This led me to pursue how std::is_empty is defined.
Ugh!
What follows is the complete implementation of std::is_empty that I can find in the VS2015 C++ library source code.
In the file type_traits is this:
// TEMPLATE CLASS is_empty
template<class _Ty>
struct is_empty _IS_EMPTY(_Ty)
{ // determine whether _Ty is an empty class
};
... and the macro _IS_EMPTY is defined in the same file:
#define _IS_EMPTY(_Ty) \
: _Cat_base<__is_empty(_Ty)>
... and at this point my luck runs dry, because I cannot find the definition of __is_empty anywhere. I have GREPped through the entire VS2015 installation directory (which includes, I think, all of the C++ library source code, in - though perhaps I'm mistaken).
I like to understand C++ internals when I want to. But ... I'm stuck on this one, and plenty of googling did not reveal the answer (though I have seen reference to intrinsics), and my digging has not ... discovered any source code.
Can someone enlighten this situation? How is std::is_empty<T> actually implemented in VS2015, or, for that matter, any other compiler?
Looks as if MSVC++ provides an intrinsic __isempty(T) rather than dealing with a library-level implementation. Since the argument type T passed to std::is_empty<T> can be final, I don't think there can be a safe library implementation and compiler help may be needed.
The only way to determine if a type T is empty in a library I can think of is this (the specialization deals with non-class types for which std::is_empty<T> is not true):
template <bool, typename T>
struct is_empty_aux: T { unsigned long long dummy; };
template <typename T>
struct is_empty_aux<false, T> { unsigned long long dummy[2]; };
template <typename T>
struct is_empty:
std::integral_constant<bool,
sizeof(is_empty_aux<std::is_class<T>::value, T>)
== sizeof(unsigned long long)> {
};
However, if T is final the inheritance in is_empty_aux is illegal. While the case of a final class can be detected using std::is_final<T> I don't see a way to determine whether its objects are empty. Thus, using a compiler intrinsic may be necessary. Compiler intrinsics are certainly necessary for some of the other type traits anyway. Compiler intrinsics are declarations/definitions somehow magically provided by the compiler: they are normally neither explicitly declared nor defined. The compiler has the necessary knowledge about types and exposing this knowledge via intrinsics is a reasonable approach.
Also ran across this question and took a look at the gcc 4.8 headers in Ubuntu 14.04. In fact there are a family of them, like __is_empty, __is_pod, __is_polymorphic, etc, used in this way
// /usr/include/c++/4.8/type_traits:516
template<typename _Tp>
struct is_empty
: public integral_constant<bool, __is_empty(_Tp)>
{};
They seem to work without including any C++ headers. I've tried g++ and clang++ to compile this code
#include <stdio.h>
struct X {};
int main()
{
printf("%d %d %d\n", __is_empty(X), __is_enum(X), __is_class(X));
return 0;
}
What I feel uncommon is that they look like functions but actually take a type as argument, instead of an instance (not work if you try X x; __is_empty(x);).
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.
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.
C++14 will allow the creation of variables that are templated. The usual example is a variable 'pi' that can be read to get the value of the mathematical constant π for various types (3 for int; the closest value possible with float, etc.)
Besides that we can have this feature just by wrapping a variable within a templated struct or class, how does this mix with type conversions? I see some overlapping.
And other than the pi example, how would it work with non-const variables? Are there any usage examples to understand how to make the most of such a feature and what its purpose is?
And other than the pi example, how would it work with non-const
variables?
Currently, it seems to instantiate the variables separately for the type. i.e., you could assign 10 to n<int> and it would be different from the template definition.
template<typename T>
T n = T(5);
int main()
{
n<int> = 10;
std::cout << n<int> << " "; // 10
std::cout << n<double> << " "; // 5
}
If the declaration is const, it is readonly. If it's a constexpr, like all constexpr declarations, it has not much use outside constexpr(ressions).
Besides that we can have this feature just by wrapping a variable
within a templated struct or class, how does this mix with type
conversions?
It's meant to be a simple proposal. I am unable to see how it affects type conversions in a significant way. As I already stated, the type of the variable is the type you instantiated the template with. i.e., decltype(n<int>) is int. decltype((double)n<int>) is double and so on.
Any usage example to understand how to make the most of such a feature
and what its purpose is?
N3651 provides a succinct rationale.
Alas, existing C++ rules do not allow a template declaration to
declare a variable. There are well known workarounds for this
problem:
• use constexpr static data members of class templates
• use constexpr function templates returning the desired values
These workarounds have been known for decades and well documented.
Standard classes such as std::numeric_limits are archetypical
examples. Although these workarounds aren’t perfect, their drawbacks
were tolerable to some degree because in the C++03 era only simple,
builtin types constants enjoyed unfettered direct and efficient
compile time support. All of that changed with the adoption of
constexpr variables in C++11, which extended the direct and efficient
support to constants of user-defined types. Now, programmers are
making constants (of class types) more and more apparent in programs.
So grow the confusion and frustrations associated with the
workarounds.
...
The main problems with "static data member" are:
• they require "duplicate" declarations: once inside the class
template, once outside the class template to provide the "real"
definition in case the con- stants is odr-used.
• programmers are both miffed and confused by the necessity of providing twice the same
declaration. By contrast, "ordinary" constant declarations do not need
duplicate declarations.
...
Well known examples in this category are probably static member
functions of numeric_limits, or functions such as
boost::constants::pi<T>(), etc. Constexpr functions templates do not
suffer the "duplicate declarations" issue that static data members
have; furthermore, they provide functional abstraction. However, they
force the programmer to chose in advance, at the definition site, how
the constants are to be delivered: either by a const reference, or by
plain non- reference type. If delivered by const reference then the
constants must be systematically be allocated in static storage; if
by non-reference type, then the constants need copying. Copying isn’t
an issue for builtin types, but it is a showstopper for user-defined
types with value semantics that aren’t just wrappers around tiny
builtin types (e.g. matrix, or integer, or bigfloat, etc.) By
contrast, "ordinary" const(expr) variables do not suffer from this
problem. A simple definition is provided, and the decision of
whether the constants actually needs to be layout out in storage only
depends on the usage, not the definition.
we can have this feature just by wrapping a variable within a templated struct or class
Yes, but that would be gratuitous syntactic salt. Not healthy for the blood pressure.
pi<double> conveys the intent better than pi<double>::value. Short and to the point. That's enough of a reason in my book to allow and encourage this syntax.
Another practical example for C++14's variable templates is when you need a function for passing something into std::accumulate:
template<typename T>
T const & (*maxer) (T const &, T const &) = std::max<T>;
std::accumulate(some.begin(), some.end(), initial, maxer<float>);
Note that using std::max<T> is insufficient because it can't deduce the exact signature. In this particular example you can use max_element instead, but the point is that there is a whole class of functions that share this behavior.
I wonder whether something along these lines would be possible: (assuming availability of template lambdas)
void some_func() {
template<typename T>
std::map<int, T> storage;
auto store = []<typename T>(int key, const T& value) { storage<T>[key] = value; };
store(0, 2);
store(1, "Hello"s);
store(2, 0.7);
// All three values are stored in a different map, according to their type.
}
Now, is this useful?
As a simpler use, notice that the initialization of pi<T> uses explicit conversion (explicit call of a unary constructor) and not uniform initialization. Which means that, given a type radians with a constructor radians(double), you can write pi<radians>.
Well, you can use this to write compile time code like this:
#include <iostream>
template <int N> const int ctSquare = N*N;
int main() {
std::cout << ctSquare<7> << std::endl;
}
This is a significant improvement over the equivalent
#include <iostream>
template <int N> struct ctSquare {
static const int value = N*N;
};
int main() {
std::cout << ctSquare<7>::value << std::endl;
}
that people used to write to perform template metaprogramming before variable templates were introduced. For non-type values, we were able to do this since C++11 with constexpr, so template variables have only the advantage of allowing computations based on types to the variable templates.
TL;DR: They don't allow us to do anything we couldn't do before, but they make template metaprogramming less of a PITA.
I have a use case here.
template<typename CT> constexpr CT MARK = '%';
template<> constexpr wchar_t MARK<wchar_t> = L'%';
which are used in a string processing template.`
template <typename CT>
void ProcessString(const std::basic_string<CT>& str)
{
auto&& markpos = str.find(MARK<CT>);
...
}
I know that compilers have much freedom in implementing std::type_info functions' behavior.
I'm thinking about using it to compare object types, so I'd like to be sure that:
std::type_info::name must return two different strings for two different types.
std::type_info::before must say that Type1 is before Type2 exclusive-or Type2 is before Type1.
// like this:
typeid(T1).before( typeid(T2) ) != typeid(T2).before( typeid(T1) )
Two different specialization of the same template class are considered different types.
Two different typedef-initions of the same type are the same type.
And finally:
Since std::type_info is not copyable, how could I store type_infos somewhere (eg: in a std::map)? The only way it to have a std::type_info always allocated somewhere (eg: on the stack or on a static/global variable) and use a pointer to it?
How fast are operator==, operator!= and before on most common compilers? I guess they should only compare a value. And how fast is typeid?
I've got a class A with a virtual bool operator==( const A& ) const. Since A has got many subclasses (some of which are unknown at compile time), I'd overload that virtual operator in any subclass B this way:
virtual bool operator==( const A &other ) const {
if( typeid(*this) != typeid(other) ) return false;
// bool B::operator==( const B &other ) const // is defined for any class B
return operator==( static_cast<B&>( other ) );
}
Is this an acceptable (and standard) way to implement such operator?
After a quick look at the documentation, I would say that :
std::type_info::name always returns two different strings for two different types, otherwise it means that the compiler lost itself while resolving types and you shouldn't use it anymore.
Reference tells : "before returns true if the type precedes the type of rhs in the collation order. The collation order is just an internal order kept by a particular implementation and is not necessarily related to inheritance relations or declaring order."
You therefore have the guarantee that no types has the same rank in the collation order.
Each instantiation of a template class is a different type. Specialization make no exceptions.
I don't really understand what you mean. If you mean something like having typedef foo bar; in two separate compilation units and that bar is the same in both, it works that way. If you mean typedef foo bar; typedef int bar;, it doesn't work (except if foo is int).
About your other questions :
You should store references to std::type_info, of wrap it somehow.
Absolutely no idea about performance, I assume that comparison operators have constant time despite of the type complexity. Before must have linear complexity depending on the number of different types used in your code.
This is really strange imho. You should overload your operator== instead of make it virtual and override it.
Standard 18.5.1 (Class type_info) :
The class type_info describes type
information generated by the
implementation. Objects of this class
effectively store a pointer to a name
for the type, and an encoded value
suitable for comparing two types for
equality or collating order. The
names, encoding rule, and collating
sequence for types are all unspecified
and may differ between programs.
From my understanding :
You don't have this guarantee regarding std:type_info::name. The standard only states that name returns an implementation-defined NTBS, and I believe a conforming implementation could very well return the same string for every type.
I don't know, and the standard isn't clear on this point, so I wouldn't rely on such behavior.
That one should be a definite 'Yes' for me
That one should be a definite 'Yes' for me
Regarding the second set of questions :
No, you cannot store a type_info. Andrei Alexandrescu proposes a TypeInfo wrapper in its Modern C++ Design book. Note that the objects returned by typeid have static storage so you can safely store pointers without worrying about object lifetime
I believe you can assume that type_info comparison are extremely efficient (there really isn't much to compare).
You can store it like this.
class my_type_info
{
public:
my_type_info(const std::type_info& info) : info_(&info){}
std::type_info get() const { return *info_;}
private:
const std::type_info* info_;
};
EDIT:
C++ standard 5.2.8.
The result of a
typeid expression is an lvalue of
static type const std::type_info...
Which means you can use it like this.
my_type_info(typeid(my_type));
The typeid function returns an lvalue (it is not temporary) and therefore the address of the returned type_info is always valid.
The current answers for questions 1 and 2 are perfectly correct, and they're essentially just details for the type_info class - no point in repeating those answers.
For questions 3 and 4, it's important to understand what precisely is a type in C++, and how they relate to names. For starters, there are a whole bunch of predefined types, and those have names: int, float, double. Next, there are some constructed types that do not have names of their own: const int, int*, const int*, int* const. There are function types int (int) and function pointer types int (*)(int).
It's sometimes useful to give a name to an unnamed type, which is possible using typedef. For instance, typedef int* pint or typedef int (*pf)(int);. This introduces a name, not a new type.
Next are user-defined types: structs, classes, unions. There's a good convention to give them names, but it's not mandatory. Don't add such a name with typedef, you can do so directly: struct Foo { }; instead of typedef struct {} Foo;. It's common to have class definitions in headers, which end up\ in multiple translation units. That does mean the class is defined more than once. This is still the same type, and therefore you aren't allowed to play tricks with macros to change the class member definitions.
A template class is not a type, it's a recipe for types. Two instantiations of a single class template are distinct types if the template arguments are different types (or values). This works recursively: Given template <typename T> struct Foo{};, Foo<Foo<int> > is the same type as Foo<Foo<Bar> > if and only if Bar is another name for the type int.
Type_info is implementation defined so I really wouldn't rely on it. However, based on my experiences using g++ and MSVC, assumptions 1,3 and 4 hold... not really sure about #2.
Is there any reason you can't use another method like this?
template<typename T, typename U>
struct is_same { static bool const result = false; };
template<typename T>
struct is_same<T, T> { static bool const result = true; };
template<typename S, typename T>
bool IsSame(const S& s, const T& t) { return is_same<S,T>::result; }
Since std::type_info is not copyable, how could I store type_infos somewhere (eg: in a std::map)? The only way it to have a std::type_info always allocated somewhere (eg: on the stack or on a static/global variable) and use a pointer to it?
This is why std::type_index exists -- it's a wrapper around a type_info & that is copyable and compares (and hashes) by using the underlying type_info operations