Use case for non-type template parameter that's not of integral/enumeration type?
Asked Answered
E

5

6

C++ allows non-type template parameters to be of integral or enumeration type (with integral including boolean and character), as well as pointers and references to arbitrary types.

I have seen integer, boolean, and enumeration parameters used widely and I appreciate their utility. I've even seen a clever use of character parameters for compile-time parsing of strings.

But I'm wondering what are some use cases for non-type template parameters that are pointers or references to arbitrary types?

Emcee answered 3/3, 2013 at 21:27 Comment(1)
will try to find a good example, but function pointer template parameters are pretty usefulVivavivace
W
4

Using a pointer-to-member-function as a template parameter makes it possible for the compiler to inline a call to that function. An example of this usage can be seen in my answer to this question: How to allow templated functor work on both member and non-member functions

In this example, the pointer-to-member-function in the template parameter enables the generation of a 'thunk' function that contains a call (inlined) to the pointer-to-member-function. A pointer to the thunk function has a generic signature (and fixed size) which enables it to be stored and copied with minimal runtime cost, unlike a pointer-to-member-function.

Wharton answered 15/7, 2013 at 12:13 Comment(7)
Interesting. I wonder whether std::bind does, or could, make use of this technique. Do you know whether std::bind implementations typically store a member function pointer in the object returned by, say, std::bind(&SomeClass::some_function, some_arg, _1)?Emcee
std::bind cannot use a member function pointer as a non-type template parameter, because it takes the member function pointer &SomeClass::some_function as a function parameter, not as a template parameter.Wharton
Sure, but the object returned by std::bind() could have the member function pointer as a non-type template parameter, and therefore avoid storing the member function pointer, right?Emcee
That would only be possible if there were an overload of std::bind that could be called with a non-type template parameter: std::bind<decltype(&SomeClass::some_function), &SomeClass::some_function>(some_arg, _1). Otherwise std::bind must store the member-function-pointer.Wharton
Right, I forgot about having to deduce the value for the template parameter. Is there any way to deduce the value of a function pointer template parameter?Emcee
Ask a new question for that, and I'll give you a more detailed answer ;)Wharton
Sure: #17736837Emcee
M
3

if you know the address of a buffer at compile time, you can make a decision (at compile time) based on its alignment, especially for things such as memcpy, this allows you to skip any run-time checking, and just jump straight to copying the data using the most efficiently sized types.

(I'm guessing) You might also be able to compile-assert that a pointer passed in is page aligned (useful for e.g. nvme protocol), though I don't know offhand what that would look like.

Mullin answered 7/3, 2013 at 19:48 Comment(0)
P
2

I think important, pointer-template-argument are Operations. (Where the more circumstantial way would be a function pointer, and the "easier" way a function object [which in turn is a type again.])

template<typename Key, class Val, bool (*CMP)(Key const&, Key const&)>
class map
{
};

template<typename KEY, class VALUE, typename CMP = std::less<KEY>>
class map
{
public:
  CMP cmp;
};

Since you do not know, which comparison to apply beforehand, you cannot build it into the container. It is required to be supplied either externally to any function that requires it or via a template.

Protolithic answered 15/7, 2013 at 2:27 Comment(5)
Are there any advantages of the first (function pointer) over the second (function object)?Emcee
I can't think of any, its the other way round. The function object is more flexible than the function pointer syntax.Protolithic
It's apparently just late enough for me to be stupid. Comment withdrawn.Fluid
@Emcee yes, there are many more functions than there are function objects in the wild. So interoperability is much improved. In addition, the type of the function pointer is specified and checked at template argument deduction, while function objects are ducktyped (barring SFINAE).Revolution
@Yakk Where the function object is still more flexible. The function pointer example needs a function that returns bool and has two arguments, accoding to the template parameter Key. The function object may well have arguments of type X, where a conversion Key -> X is valid and it may return a type that is convertible to bool.Protolithic
F
2

I've used an intrusive (data pointers stored in the data elements) singly-linked list before, that was parameterized by a pointer to data member indicating where the list stored its links. That parameterization makes it possible to store the same data element in multiple lists if they use different link members:

template <class T, T* (T::*Link)> class ilist;

struct Node {
  Node* a_next;
  Node* b_next;
};

typedef ilist<Node, &Node::a_next> a_list;
typedef ilist<Node, &Node::b_next> b_list;
Fluid answered 17/7, 2013 at 7:4 Comment(0)
W
-1

Here is a useful example of non-integral template parameters. Some predeclarations (not all, but just enough to get the idea):

template <bool flag, class T, class F> struct SelectType
{
    typedef T Result;
};
template <class T, class F> struct SelectType<false, T, F>
{
    typedef F Result;
};

#define PARAMETER( selector, type ) typename SelectType<TypeTraits<T>::selector, type,

#define PTR_TRAITS typename ::Linderdaum::Utils::TypeTraits<T>::PointeeType
#define REF_TRAITS typename ::Linderdaum::Utils::TypeTraits<T>::ReferredType

using namespace ::Linderdaum::Utils;

template <class T> struct ParameterType
{
    typedef
    PARAMETER( IsString,         clStringParameter              )
    PARAMETER( IsReference,      clPODParameter<REF_TRAITS>     )
    PARAMETER( IsPointer,        clPointerParameter<PTR_TRAITS> )
    PARAMETER( IsFundamental,    clPODParameter<T>              )
    // default
    clPODParameter<T>
    >::Result
    >::Result
    >::Result
    >::Result
    Type;
};

And the actual usage code:

 clParametersList Params;

 ParameterType<const LString& >::Type Param0;
 ParameterType<size_t >::Type Param1;
 ParameterType<clDownloadCompleteCallback >::Type Param2;

 Param0.ReadValue( &P0 );
 Param1.ReadValue( &P1 );
 Param2.ReadValue( &P2 );

 Params.push_back( &Param0 );
 Params.push_back( &Param1 );
 Params.push_back( &Param2 );
Wanigan answered 16/7, 2013 at 14:38 Comment(1)
Where is the non-integral non-type template parameter in this code?Emcee

© 2022 - 2024 — McMap. All rights reserved.