Using template parameter vs constructor argument - c++

If i have for example some template struct, and i want to give it a user-defined size for some member i can do it by passing a value to constructor like so:
template <typename T>
struct Foo {
int m_size;
Foo(int u_size)
:
m_size {u_size}
{
}
};
and i can also do it by having a non-type template parameter (kinda the way std::array does it), like so:
template <typename T, int u_size>
struct Foo {
int m_size;
Foo()
:
m_size {u_size}
{
}
};
My question is, what is the difference between these two methods, and when is it useful to use either of them?

You are using u_size to determine the initializer for the member m_size. The difference between the two examples boils down to Foo<T,i> being a different type than Foo<T,j>. Other than being instantiations of the same template (which usually does not mean much) they are totally unrelated. On the other hand, in the first case, any Foo<T> is of the same type Foo<T>, no matter what was the initial value for the member.
If m_size changes at runtime, it is questionable to require an instance that uses 42 as initial value and an instance that uses 24 as initial value to be of different types.
Maybe m_size does not change at runtime, then it can make sense to have the initial value as part of the type (like eg with std::array). Maybe it does change at runtime, but nevertheless there are reasons to have the initial value baked into the type. There is no "better" Or "worse", it depends what you want/need.

Related

Safe to reference a C++ template type having a template parameter that's not compatible with the template?

In the following code sample, I define a class DT (my default type) which I want to be able to pass as the parameter(s) for an arbitrary template. In this example, I pass DT as the key and value parameters for std::map. I don't actually ever try to instantiate map<DT,DT>, I just want to use map<DT,DT> as the template parameter for a templatized function (in this sample, the function f()) that never actually references the type -- it's only used to produce a type-specific instance of the function. (Note that you can't actually instantiate a std::map<DT,DT> since the key of a map must be comparable, but DT is not.)
#include <iostream>
#include <map>
using namespace std;
class DT {};
template <typename T>
string f() {
return "foo";
}
int main() {
cout << f<map<DT,DT>>() << endl;
return 0;
}
This seems to work fine using g++. I even tried passing DT for all four map parameters (overriding the default comparator, and allocator types). Still works. But I'm worried that this technique might fail with some other template, or with some other compiler. So my question is: is this always safe for any template on any c++ compiler compliant with, say, the c++11 standard (and later standards). In other words, is it always safe to pass a completely incompatible type as the parameter for a template, so long as you never try to instantiate that template?
If you're wondering why on earth I'd want to do such a thing, I'm trying to setup a class where I can store type-dependent configuration strings. It will have these two methods:
template<typename T>
const string& get<T>() const;
template<typename T>
void set<T>(const string& value);
I have it largely working to my satisfaction. It has several nice features. For example, types int, const int, int&, const int&, etc are all treated as the same type (which is what I want). And you can store a configuration string for a base class that is later retrievable by a derived type if no entry is found for the more specific derived type. But for the case of say, a std::map, I'd like to be able to store a default configuration string using type map<DT,DT> that will later be returned as a match for any map<Key,Value> when no entry is found for the specific map type at hand. If the above code is valid, then I think I can produce the desired behavior.
Unfortunately, I believe, the standard does not guarantee that std::map<DT, DT> will not be instantiated. [temp.inst]/1 only specifies that
Unless a class template specialization has been explicitly instantiated or explicitly specialized, the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program. […]
Note that this only tells us when a template is guaranteed to be instantiated, it does not give any guarantees that the template will not be instantiated if such an instantiation is not required. [temp.inst]/10 only gives such a guarantee for
[…] a function template, a variable template, a member template, a non-virtual member function, a member class, a static data member of a class template, or a substatement of a constexpr if statement ([stmt.if]), unless such instantiation is required. […]
Note the absence of class templates from this list. Thus, I believe, a compiler would theoretically be allowed to instantiate std::map<DT, DT> even thought it would not be necessary to do so. If instantiating the template std::map with DT as key and value type would be invalid, you'd have a problem. I am unable to find any guarantees concerning instantiating std::map with a key type that does not support a comparison operator. While I would expect this to just work with basically any implementation, I do think that an implementation would theoretically be allowed to, e.g., have a static_assert that checks whether the key type does fulfill the necessary requirements. [res.on.functions]/1 would seem to apply (emphasis mine):
In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ standard library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.
Thus, I think that, strictly speaking, the standard does not guarantee that using std::map<DT, DT> will work…
If you simply want to use std::map<DT, DT> as a sort of tag type to indicate a special case, I would suggest to just not use std::map but something else, for example:
template <typename, typename>
struct default_config_tag;
and then default_config_tag<DT, DT> or just DT as your tag (not sure if you need the argument to be an instance of a template with two type parameters) should be sufficient…
You've already got the answer to your question, but the context of the question is likewise interesting for readers of this post, so I thought it'd be valuable to mention that you may use tag dispatching for a use case such as your own, to:
Set up default configuration strings, at compile time, for specific types (e.g. int) or groups of types (e.g. std::map<K, V> for generic K and V)
Without tag dispatching, this can be tricky as you may not partially specialize a function template.
E.g.:
#include <map>
#include <string>
#include <iostream>
template <typename T>
class Config {
public:
static const std::string& get() { return Config::getString(); }
static void set(const std::string& value) { Config::getString() = value; }
Config(Config const&) = delete;
void operator=(Config const&) = delete;
private:
static std::string& getString() {
static std::string s(defaultString(dispatch_tag<T>{}));
return s;
}
template <typename U>
struct dispatch_tag {};
// Default string unless specified for specific types below.
template <typename U = T>
static constexpr std::string_view defaultString(dispatch_tag<U>) {
return "default";
}
// Default config strings for a select number of types.
static constexpr std::string_view defaultString(dispatch_tag<int>) {
return "default int";
}
template <typename K, typename V>
static constexpr std::string_view defaultString(
dispatch_tag<std::map<K, V>>) {
return "default map";
}
};
int main() {
std::cout << Config<int>::get() << "\n"; // default int
std::cout << Config<std::string>::get() << "\n"; // default
std::cout << Config<std::map<int, int>>::get() << "\n"; // default map
Config<int>::set("custom int");
Config<std::map<int, int>>::set("custom int-int map");
std::cout << Config<int>::get() << "\n"; // custom int
std::cout << Config<std::map<int, int>>::get() << "\n"; // custom int-int map
std::cout << Config<std::map<int, char>>::get() << "\n"; // default map
}
This does not solve, however, that you would like to (based on your comments to your own post) specify, at run-time, the value of the fall-back default configuration string for generic types (say, std::map<K, V>).

Is there a way to return a constexpr object based on the type of runtime values?

Let's take a template class:
template<typename T>
class A{};
And a helper function that instantiates an A with type deduction:
template<typename T>
constexpr A<T> makeA(T)
{
return A<T>();
}
And finally a runtime value, but whose type (int) is known at compile time:
int i = //... retrieving a runtime value;
The following does not compile, although it only uses the type of i, and not its value:
constexpr auto a = makeA(i);
The error being: the value of 'i' is not usable in a constant expression.
I understand that i can't be passed since its value is unknown at compile-time, but on the other hand makeA totally discards this value.
So how can I instantiate a constexpr A templated on the type of i, without having to spell out A<int> or A<decltype(i)>?
I'd imagine there must be a way since all the information is available at compile time.
While all the information is available at compile-time, you're trying to really make it look like you're passing an object to the function at runtime. There is simply no way to do this other than the alternative methods you've already listed.
makeA<int>() seems perfectly reasonable to me (if remarkably contrived, but I'll assume your real T is more complex), and is consistent with "maker" functions already provided by the standard library.

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.

getting an neutral element for std::multiply and std::plus

While implementing a template class whose constructor takes:
vector of functions (functions are of type std::function<bool(const T&)>)
std::binary_function<bool,bool,bool> that I will use to accumulate results of applications of vector from 1) to certain value.
I want to be able to use std::plus() and std::multiplies() as second template parameter, but the problem is that depending on the function I need a corresponding neutral element (for std accumulate init value). For AND (std::multiplies) I need true (aka 1), for OR (std::plus) I need false (aka 0). I know I could just specialize the template and problem solved but I wonder if there is a way to get neutral element for a built in STL function.
If you're using gcc, you can use __gnu_cxx::identity_element, which is doing exactly what you're asking for.
If you're not, I don't think there is a general solution, as if there was, gcc wouldn't have implemented their own - you might just rewrite their implementation (which is actually just a couple of template specializations, as you expected).
Edit: the source code for this is on lines 78-98 of this file.
The usual solution here is traits. Rather than instantiate your
template on std::plus or whatever, you would instantiate it on a
traits class, which defines a typedef for std::plus, plus an
identity element (static const, with initializer), and anything else you
need. Something like:
struct OpTraitsAdd
{
typedef std::plus<int> Op;
static int const identity = 0;
};
struct OpTraitsMult
{
typedef std::multiplies<int> Op;
static int const identity = 1;
};
It's also possible to obtain the traits from the standard operator,
using explicit specialization:
template <typename Op> struct OpTraits;
template<>
struct OpTraits<std::plus<int> >
{
static int const identity = 0;
};
template<>
struct OpTraits<std::multiplies<int> >
{
static int const identity = 1;
};
In this case, you would instantiate your class over the operator, and
use OpTraits<Op>::identity when needed.
In both cases, of course, you do have to provide all of the necessary
traits, either as independent classes or as template specializations.
If the only two identity elements you need are 0 and 1, you might be
able to do it automatically with something like:
template <bool idIs0> struct IdImpl;
template<>
struct IdImpl<false>
{
static int value = 1;
};
template<>
struct IdImpl<true>
{
static int value = 0;
};
template <typename Op>
struct Id
{
static int value = ItImpl<Op(1, 0) == 1>::value;
};
This will not work pre-C++11, since Op(1, 0) is not a constant
epxression. I'm not sure about C++11; but I think that if
Op::operator() is declared constexpr, it should work. (I'd only
bother trying if I had to cover a lot of operators, including some that
clients might provide.)
Just to pick out one point from James's answer (and my comment to it). I think it deserves to be considered separately.
If you want to you can compute the identity at runtime, assuming that there really is an identity. It's !func(true, false).
If func::operator() is available and free of side-effects, as it is for std::plus and std::multiplies, then presumably any sensible compiler will actually compute that at compile time. But it doesn't need the value at compile time, so now your template could (if the caller wants) accept a std::function<bool(bool,bool)> instead of needing to know the actual accumulate operation at compile-time.

Reason for using non-type template parameter instead of regular parameter?

In C++ you can create templates using a non-type template parameter like this:
template< int I >
void add( int& value )
{
value += I;
}
int main( int argc, char** argv )
{
int i = 10;
add< 5 >( i );
std::cout << i << std::endl;
}
Which prints "15" to cout. What is the use for this? Is there any reason for using a non-type template parameter instead of something more conventional like:
void add( int& value, int amount )
{
value += amount;
}
Sorry if this has already been asked (I looked but couldn't find anything).
There are many applications for non-type template arguments; here are a few:
You can use non-type arguments to implement generic types representing fixed-sized arrays or matrices. For example, you might parameterize a Matrix type over its dimensions, so you could make a Matrix<4, 3> or a Matrix<2, 2>. If you then define overloaded operators for these types correctly, you can prevent accidental errors from adding or multiplying matrices of incorrect dimensions, and can make functions that explicitly communicate the expected dimensions of the matrices they accept. This prevents a huge class of runtime errors from occur by detecting the violations at compile-time.
You can use non-type arguments to implement compile-time function evaluation through template metaprogramming. For example, here's a simple template that computes factorial at compile-time:
template <unsigned n> struct Factorial {
enum {
result = n * Factorial<n - 1>::result
};
};
template <> struct Factorial<0> {
enum {
result = 1
};
};
This allows you to write code like Factorial<10>::result to obtain, at compile-time, the value of 10!. This can prevent extra code execution at runtime.
Additionally, you can use non-type arguments to implement compile-time dimensional analysis, which allows you to define types for kilograms, meters, seconds, etc. such that the compiler can ensure that you don't accidentally use kilograms where you meant meters, etc.
Hope this helps!
You're probably right in this case, but there are cases where you need to know this information at compile time:
But how about this?
template <std::size_t N>
std::array<int, N> get_array() { ... }
std::array needs to know its size at compile time (as it is allocated on the stack).
You can't do something like this:
std::array<int>(5);
Well, this the typical choice between compile-time polymorphism and run-time polymorphism.
From the wording of your question in appears that you see nothing unusual in "ordinary" template parameters, while perceiving non-type parameters as something strange and/or redundant. In reality the same issue can be applied to template type parameters (what you called "ordinary" parameters) as well. Identical functionality can often be implemented either through polymorphic classes with virtual functions (run-time polymorphism) or through template type parameters (compile-time polymorphism). One can also ask why we need template type parameters, since virtually everything can be implemented using polymorphic classes.
In case of non-type parameters, you might want to have something like this one day
template <int N> void foo(char (&array)[N]) {
...
}
which cannot be implemented with a run-time value.
In that particular instance, there's not really any advantage. But using template parameters like that, you can do a lot of things you couldn't do otherwise, like effectively bind variables to functions (like boost::bind), specify the size of a compile-time array in a function or class (std::array being a ready example of that), etc.
For instance, with that function, you write a function like
template<typename T>
void apply(T f) {
f(somenum);
}
Then you can pass apply a function:
apply(&add<23>);
That's an extremely simple example, but it demonstrates the principle. More advanced applications include applying functions to every value in a collection, calculating things like the factorial of a function at compile time, and more.
You couldn't do any of that any other way.
There are lots of reasons, like doing template metaprogramming (check Boost.MPL). But there is no need to go that far, C++11's std::tuple has an accessor std::get<i> that needs to be indexed at compile time, since the result is dependent on the index.
The most frequent use for a value parameter that I can think of is std::get<N>, which retrieves the Nth element of a std::tuple<Args...>. The second-most frequent use would be std::integral_constant and its main derivatives std::true_type and std::false_type, which are ubiquitous in any sort of trait classes. In fact, type traits are absolutely replete with value template parameters. In particular, there are SFINAE techniques which leverage a template of signature <typename T, T> to check for the existence of a class member.